【展开说说】InnoDB 索引页结构

前言

页是 InnoDB 管理存储空间的基本单位,一个页的大小一般是 16KB。InnoDB 为了不同的目的而设计了多种不同类型的页,比如存放表空间头部信息的页、存放 ChangeBuffer 信息的页、存放 INODE 信息的页、存放 undo 日志信息的页,等等。我们主要展开说说存放表中记录的那种类型的页,官方称这种存放记录的页为索引(INDEX)页。

数据页结构概览

数据页代表的这块 16KB 大小的存储空间可以划分为多个部分,不同部分有不同的功能,如下图所示:
在这里插入图片描述
每个部分存储的内容如下表所示:
在这里插入图片描述

记录在页中存储

在页的 7 个组成部分中,我们自己存储的记录会按照指定的行格式存储到 User Records 部分。但是在一开始生成页的时候,其实并没有 User Records 部分,每当插入一条记录时,都会从 Free Space 部分(也就是尚未使用的存储空间)申请一个记录大小的空间,并将这个空间划分到 User Records 部分。当 Free Space 部分的空间全部被 User Records 部分替代掉之后,也就意味着这个页使用完了,此时如果还有新的记录插入,就需要去申请新的页了。这个过程如下图所示:
在这里插入图片描述

Page Directory(页目录)

页目录是 InnoDB 为页中的记录设计的一个目录,制作过程如下:

  1. 将所有正常的记录(包括 Infimum 和 Supremum 记录,但不包括已经移除到垃圾链表的记录)划分为几个组。
  2. 每个组的最后一条记录的头信息中的 n_owned 表示该组内共有几条记录。
  3. 将每个组中最后一条记录在页面中的地址偏移量(就是该记录的真实数据与页面中第 0 个字节之间的距离)单独提取出来,按顺序存储到靠近页尾部的地方。这个地方就是 Page Directory。页目录中的这些地址偏移量称为槽(Slot),每个槽占用 2 字节。页目录就是由多个槽组成的。

假设现在 page_demo 表中正常的记录共有 6 条。InnoDB 会把它们分成 2 个组,第一组只有一个 Infimum 记录,第二组是剩余的 5 条记录。2 个组就对应着 2 个槽,每个槽中存放每个组中最大的那条记录在页面中的地址偏移量,如下图所示:
在这里插入图片描述
先往 page_demo 表中插入 16 条数据,我们看看怎么在页目录中查找数据:
在这里插入图片描述
假设我们要查找主键为 6 的记录,过程是这样的:

  1. 计算中间槽的位置:(0+4)/2=2,查看槽 2 对应记录的主键值为 8,又因为 8>6,所以设置 high=2,low 保持不变。
  2. 重新计算中间槽的位置,(0+2)/2=1,查看槽 1 对应记录的主键值为 4;又因为 4 < 6,所以设置 low=1,high 保持不变。
  3. 因为 high - low 的值为 1,所以确定主键值为 6 的记录在槽 2 对应的组中。此时需要找到槽 2 所在分组中主键值最小的那条记录,然后沿着单向链表遍历槽 2 中的记录。但是,每个槽对应的记录都是该组中主键值最大的记录,这里槽 2 对应的记录是主键值为 8 的记录。所以我们要先找到槽 1 对应的记录(主键为 4),这条记录的下一条记录就是槽 2 所在分组中主键值最小的记录,其主键值为 5。所以,我们可以从这条主键值为 5 的记录出发,遍历槽 2 中的各条记录,直到找到主键值为 6 的那条记录即可。由于一个组中包含的记录条数最多是 8 条,所以遍历一个组中的记录的代价是很小的。

综上,在一个数据页中查找指定主键值的记录时,过程分为两步:

  1. 通过二分法确定该记录所在分组对应的槽,然后找到该槽所在分组中主键值最小的那条记录(上个槽中最大记录的下一条)。
  2. 通过记录的 next_record 属性遍历该槽所在的组中的各个记录。

Page Header(页面头部)

lnnoDB 为了能得到存储在数据页中的记录的状态信息,比如数据页中已经存储了多少条记录、Free Space 在页面中的地址偏移量、页目录中存储了多少个槽等,特意在数据页中定义了一个名为 Page Header 的部分,它是页结构的第2部分,占用固定的 56 字节,专门存储各种状态信息。Page Header 中各个字节的具体用途如下表所示:
在这里插入图片描述

  • PAGE_DIRECTION:假如新插入的一条记录的主键值比上一条记录的主键值大,我们说这条记录的插入方向是右边,反之则是左边。用来表示最后一条记录插入方向的状态就是PAGE_DIRECTION。
  • PAGE_N_DIRECTION:假设连续几次插入新记录的方向都是一致的,InnoDB 会把沿着同一个方向插入记录的条数记下来,这个条数就用 PAGE_N_DIRECTION 状态表示。当然,如果最后一条记录的插入方向发生了改变,这个状态的值会被消零后重新统计。

File Header(文件头部)

File Header 通用于各种类型的页,也就是说各种类型的页都会以 File Header 作为第一个组成部分,它描述了一些通用于各种页的信息,比如这个页的编号是多少,它的上一个页和下一个页是谁,等等。File Header 部分占用固定的 38 字节,由下表所示的内容组成:
在这里插入图片描述

  • FIL_PAGE_SPACE_OR_CHKSUM:这个属性代表当前页面的校验和(checksum)。对于一个很长的字节串来说,我们会通过某种算法计算出一个比较短的值来代表这个很长的字节串,这个比较短的值就称为校验和。这样在比较两个很长的字节串之前,先比较这两个长字节串的校验和。如果校验和都不一样,则两个长字节串肯定是不同的,这样就省去了直接比较两个长字节串的时间损耗。
  • FIL_PAGE_OFFSET:每一个页都有一个单独的页号,InnoDB 通过页号来唯一定位一个页。
  • FIL_PAGE_TYPE:表示当前页的类型。其他类型的页如下表所示:在这里插入图片描述
  • FIL_PAGE_PREV 和 FIL_PAGE_NEXT:分别代表本索引页的上一页和下一页,并不是所有的页都有上一页和下一页的属性。

File Trailer(文件尾部)

File Trailer 与 File Header 类似,都通用于所有类型的页。这个部分由 8 字节组成,可以分成 2 个小部分:

  1. 前 4 字节代表页的校验和。这个部分与 File Header 中的校验和相对应。每当一个页面在内存中发生修改时,在刷新之前就要把页面的校验和算出来。因为 File Header 在页面的前边,所以 File Header 中的校验和会被首先刷新到磁盘,当完全写完后,校验和也会被写到页的尾部。如果页面刷新成功,则页首和页尾的校验和应该是一致的。如果刷新了一部分后断电了,那么 File Header 中的校验和就代表着己经修改过的页,而 File Trailer 中的校验和代表着原先的页,二者不同则意味着刷新期间发生了错误。
  2. 后 4 字节代表页面被最后修改时对应的 LSN 的后 4 字节,正常情况下应该与 File Header 部分的 FIL_PAGE_LSN 的后 4 字节相同。这个部分也是用于校验页的完整性。

总结

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值