MySQL数据库——索引02

索引

  1. InnoDB中的索引方案

    1. 迭代1次:目录项纪录的页

    上边称为一个简易的索引方案,是因为我们为了在根据主键值进行查找时使用二分法快速定位具体的目录项而假设所有目录项都可以在物理存储器上连续存储,但是有几个问题:

    • InnoDB是使用页作为管理储存空间的基本单位,最多保证16KB的连续存储空间,需要非常大的连续的存储空间才能把所有的目录项都放下,这对记录数量非常多的表是不现实的。
    • 我们时常会对记录进行增删,假设我们把页28中的记录都删除了,那就意味着目录项2也就没有存在的必要了,这就需要把目录项2后的目录都向前移动一下,这样牵一发而动全身的操作效率很差。

    所以,我们需要一种可以灵活管理所有目录项的方式。我们发现目录项其实长得跟我们的用户记录差不多,只不过目录项的两个列是主键和页号而已,为了和用户记录做一下区分,我们把这些用来表示目录项的记录称为目录项记录。那InnoDB怎么区分一条记录是普通的用户记录还是目录项记录呢?使用记录头信息里的record_type属性,它的各个取值代表的意思如下:

    • 0:普通的用户记录
    • 1:目录项记录
    • 2:最小记录
    • 3:最大记录

    我们把前边使用到的目录项放到数据页中的样子就是这样:

    image-20220328233409149

    从图中可以看出来,我们新分配了一个编号为30的页来专门存储目录项记录。这里再次强调目录项记录和普通的用户记录的不同点

    • 目录项记录的record_type值是1,而普通用户记录的record_type值是0
    • 目录项记录只有主键值和页的编号两个列,而普通的用户记录的列是用户自己定义的,可能包含很多列,另外还有InnoDB自己添加的隐藏列。
    • 了解:记录头信息里还有一个叫min_rec_mask的属性,只有在存 目录项记录 的页中的主键值最小的目录项记录 的 min_rec_mask 值为1,其他别的记录的min_rec_mask值都是 0 。

    **相同点:**两者用的是一样的数据页,都会为主键值生成Page Directory(页目录),从而在按照主键值进行查找时可以使用二分法来加快查询速度。

    现在以查找主键为 20 的记录为例,根据某个主键值去查找记录的步骤就可以大致拆分成下边两步:

    1. 先到存储目录项记录的页,也就是页30中通过二分法快速定位到对应目录项,因为 12 < 20 < 209 ,所以定位到对应的记录所在的页就是页9。
    2. 再到存储用户记录的页9中根据二分法快速定位到主键值为20的用户记录。
  2. 迭代2次:多个目录项纪录的页

    虽然说目录项记录中只存储主键值和对应的页号,比用户记录需要的存储空间小多了,但是不论怎么说一个页只有16KB大小,能存放的目录项记录也是有限的,那如果表中的数据太多,以至于一个数据页不足以存放所有的目录项记录。

    这里我们假设一个存储目录项的记录的页最多只能存放4条目录项记录,所以如果此时我们再向上图插入一条主键值为320的用户记录的话,那就需要分配一个新的存储目录项的页:

    image-20220330220039572

    从图中可以看出,我们插入了一条主键值为320的用户记录之后需要两个新的数据页:

    • 为存储该用户记录而新生成了页31 。
    • 因为原先存储目录项记录的页30的容量已满 (我们前边假设只能存储4条目录项记录),所以不得不需要一个新的 页32 来存放 页31 对应的目录项。

    现在因为存储目录项记录的页不止一个,所以如果我们想根据主键值查找一条用户记录大致需要3个步骤,以查找主键值为20的记录为例:

    1. 确定目录项记录页

      我们现在的存储目录项记录的页有两个,即页30和页32,又因为页30表示的目录项的主键值的范围是 [1, 320) ,页32表示的目录项的主键值不小于320 ,所以主键值为20的记录对应的目录项记录在页30中。

    2. 通过目录项记录页确定用户记录真实所在的页

      在一个存储目录项记录的页中通过主键值定位一条目录项记录的方式说过了。

    3. 在真实存储用户记录的页中定位到具体的记录。

  3. 迭代3次:目录项记录页的目录页

    问题来了,在这个查询步骤的第1步中我们需要定位存储目录项记录的页,但是这些页是不连续的,如果我们表中的数据非常多则会产生很多存储目录项记录的页,那我们怎么根据主键值快速定位一个存储目录项的记录的页呢?那就为这些存储目录项记录的页再生成一个更高级的目录,就像一个多级目录一样,大目录里嵌套小目录,小目录里才是实际的数据。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5iebk95u-1648651527660)(https://cdn.jsdelivr.net/gh/CerberusDong/pictures/image-20220330221912197.png)]

    如图,我们生成了一个存储更高级目录项的页33,这个页中的两条记录分别代表页30和页32,如果用户记录的主键值在 [1, 320) 之间,则到页30中查找更详细的目录项记录,如果主键值不小于320的话,就到页32中查找更详细的目录项记录。

    我们可以用下边这个图来描述它:

    image-20220330222101776

    这个数据结构,它的名称是B+树

  4. B+Tree

    不论是存放用户记录的数据页,还是存目录项记录的数据页,我们都把它们存放到B+树这个数据结构中了,所以我们也称这些数据页为节点。从图中可以看出,我们的实际用户记录其实都存放在B+树的最底层的节点上,这些节点也被称为叶子节点,其余用来存放目录项的节点称为非叶子节点或者内节点,其中B+树最上边的那个节点也称为根节点。

    一个B+树的节点其实可以分成好多层,规定最下边的那层,也就是存放我们用户记录的那层为第0层,之后依次往上加。之前我们做了一个非常极端的假设:存放用户记录的页最多存放3条记录 ,存放目录项记录的页最多存放4条记录。其实真实环境中一个页存放的记录数量是非常大的,假设所有存放用户记录的叶子节点代表的数据页可以存放100条用户记录,所有存放目录项记录的内节点代表的数据页可以存

    放1000条目录项记录 ,那么:

    • 如果B+树只有1层,也就是只有1个用于存放用户记录的节点,最多能存放100条记录。
    • 如果B+树有2层,最多能存放1000×100=10,0000条记录。
    • 如果B+树有3层,最多能存放1000×1000×100=1,0000,0000条记录。
    • 如果B+树有4层,最多能存放1000×1000×1000×100=1000,0000,0000条记录。相当多的记录!!!

    你的表里能存放100000000000条记录吗?所以一般情况下,我们用到的B+树都不会超过4层 ,那我们通过主键值去查找某条记录最多只需要做4个页面内的查找(查找3个目录项页和一个用户记录页),又因为在每个页面内有所谓的 Page Directory (页目录),所以在页面内也可以通过二分法实现快速定位记录。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值