持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第12天,点击查看活动详情
前言
之前有聊过我们存储的数据最终会按照指定的行格式User Records部分,记录按照主键从小到大的顺序生成一个单向链表,如果想根据主键查找页中的某条记录,该如何快速定位到该条记录?最笨的方法就是从最小记录(Infimum)记录开始,沿着单向链表一直往后找,直到找到想要查找的记录为止,这样不是不可行,但是一旦数据多了,遍历耗时就会非常长,本文来看一下InnoDB内部是如何设计的,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教。
页目录和槽
我们平时在看书的时候,一般会先看目录,找到需要查找的内容对应书的页码,然后到对应页码的页面查看内容,没错,InnoDB在设计的时候也是借鉴这种思想,给每一页添加一个目录,可以通过该目录快速确定记录所在的位置,我们称该目录为页目录。废话不多说,来看下是如何实现的。
过程如下:
- 将所有正常的记录(包括最大记录和最小记录,不包括标记为已删除的记录)划分为若干组。
- 每个组的最后一条记录(也就是组内最大的那条记录)的头信息中的
n_owned属性表示该组内共有几条记录。 - 将每个组的最后一条记录的地址偏移量单独提取出来按顺序存储到靠近页的尾部的地方,这个地方就是所谓的页目录(Page Directory) 。页面目录中的这些地址偏移量被称为槽(Slot) 。所以页目录是由
槽组成的,每个槽占用 2 字节。
我们通过示意图看下可能更清晰:
当然,页面中记录分组也不是随便分的,其也设定了一些规则,如下:
- 对于最小记录(Infimum)所在的槽上仅能有一条记录。
- 对于最大记录(Supremum)所在的槽上可以有1-8条直接的记录。
- 其余的中间槽上可以有4-8条记录。
新增数据时槽是如何变化的?
- 初始情况下会有两个槽,也就是最小记录(Infimum)和最大记录(Supremum)所对应的。
- 当有数据插入时,找到当前记录与槽对应记录的主键差值最小的槽,把槽对应记录的
n_owned+1,直到该组中的记录数等于8. - 当一个槽的
n_owned = 8时,再加入记录时,将该槽分成两组,分别为前4条记录,后5条记录,并且新增加一个槽。
页目录查找记录的过程
当一页的记录多了之后如何定位所在的槽和对应的记录呢?
- 根据主键通过二分法定位到具体的槽,
- 定位到槽后,通过前一个槽的最后一条记录计算出当前槽的第一条记录和最后一条记录,
- 通过记录的
next_record遍历查询。
小结
为了快速定位页中的某条记录,InnoDB把每页中的记录划分为若干个组,每个组的最后一个记录的地址偏移量作为一个槽,存放在页目录(Page Directory)中,在一个页中根据主键查找记录分为三步:
- 通过二分法确定该记录所在的槽。
- 定位到槽后,通过前一个槽的最后一条记录计算出当前槽的第一条记录和最后一条记录,
- 通过记录的
next_record遍历查询。