十四、索引基础
数据页的物理存储
数据行是存放在数据页中的,而实际上数据页在磁盘文件中就是一些连续的数据,数据页之间通过两个上下指针连接指向前后数据页的物理地址,形成双向链表。然后数据页中存储的数据行按照主键大小排序存储,每一行数据都保存一个指针指向下一行数据位置,形成单向链表。

数据页的页目录
之前说过,数据页的数据结构包含了一个页内数据行的页目录。Innodb 在存储数据时会把页内的数据行(不包含已加入删除链表的记录)分为多个记录组,每个记录组中的最后一条记录的头信息 n_owned 属性表示该组的记录数。然后将每组最后一条记录的地址偏移量提取出来存放到页目录,这个偏移量就是该记录与数据页中第 0 个字节的长度,这些地址偏移量被称为槽,每个槽占用 2 字节,页目录就是由多个槽组成的。
而实际上存储的数据行还会存在两行伪记录 Infimum 和 Supremum,所以 Innodb 对于行分组是这样规定的:
1. 对于 Infimum 所在的分组只能有一条记录
2. Supremum 所在的分组只能在 1~8 条之间
3. 剩下的分组,记录条数范围只能是 4~8 之间

页分裂
一般情况下,我们会设置表主键ID自增长,这时当数据页写满后新的数据行会自动写入新的数据页,并且新数据页中的记录主键永远比旧数据页的主键大。但有些时候会使用自定义的主键ID,这时就无法保证新数据页中的记录主键永远比旧数据页中的记录主键大了,在查询时就会出现一些问题。所以需要一个页分裂的过程,就是在增加一个新数据页时,会把主键值较大的移动到新数据页,新数据页中主键较小的记录移动到旧数据页,以此保证每个数据页中的主键都是有序的。为后续通过索引查询提供保障。

页目录二分查找
数据页中的数据行都是根据主键大小进行排序的,由于每个槽都记录了一组数据行最大 ID 在数据页中的偏移量,所以只需要根据这些偏移量使用二分查找定位到需要查找的数据在哪个槽,然后从槽内的数据组最小数据行通过指针遍历即可。比如现在每个组有 4 条数据,要查找 ID 为 6 的数据行:
那么初始情况下 low=0,high=4:
1. 计算中间槽的位置,(0+4)/ 2=2,于是查看槽 2 对应记录的主键值为 8,因为 8 > 6,所以 high = 2,low 不变。
2. 重新计算中间槽位置,(0+2)/ 2=1,于是查看槽 1 对应记录的主键为4,因为 4 < 6,所以 high 不变,low = 1。
3. 因为 high - low = 1,所以确定主键值为6 的记录就在槽 2 对应的组中。接着找到该组中主键最小的记录,沿着单链表向后遍历,最终找到主键 6 的记录。
组中主键最小的记录就是上一个组最大记录的下一条记录,然后通过这条记录的指针遍历组内的记录即可。这就是通过页目录进行的主键查找。
全表扫描
刚才的是通过页目录的主键进行查找的,但也有些时候是通过某些关键字去查找记录的,那么就无法使用页目录去检索了。这里查找的时候,就是把整个数据页加载到 Buffer pool 缓存页,然后页内部的单向链表来遍历,如果数据不在这个数据页那么就根据数据页的双向链表加载下一个数据页,如此循环遍历,最糟糕的情况就是遍历全表才能找到需要的数据,这就是全表扫描。

主键查找
现在通过主键去查找记录,在不知道主键在哪个数据页的情况下如何查找呢?唯一的办法就是遍历所有的数据页。在数据量大的场景下,这无疑是极为耗时的。所以Innodb 为提高查询效率,把每个数据页中最小主键和所在的页号组成索引目录。比如要查找主键 ID 为 2 的记录,就可以在索引目录中通过二分查找法定位到数据页,然后在页目录中找到记录所在的组,遍历即可。所以只要找到数据所在的数据页,一切都好办了。

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)