5.2 数据页结构快览

image.png
其中各部分的作用:

image.png

5.3 记录在页中的存储

我们自己存储的记录会按照指定的行格式存储到 User Records 部分。一开始生成页的时候没有 User Records 部分,每当插入一条记录都会从 Free Space 部分申请一个记录大小的空间划分到User Records 部分

当 Free Space 部分空间全部被 User Records 部分替代后,就意味着这个页用完了;如果还有新记录插入,就要另外申请页面了:
image.png

记录头信息
image.png
其图形化呈现如下:
image.png

  • deleted_flag:用来标记当前记录是否被删除。为 0 表示没有被删除,为 1 表示被删除了。其实被删除的记录仍然还在磁盘上,因为在移除它们后,还需要在磁盘上重新排列其他的记录,这会带来性能消耗,所以使用懒惰删除,打一个删除标记即可。所有被删除的记录会组成一个垃圾链表,记录该链表中占用的空间称为可重用空间。之后若有新记录插入表,可能使用可重用空间的内存。

  • min_rec_flag:B+树的每层非叶子节点中的最小的目录项记录都会添加到该标记。

  • n_owned:(在后面介绍)

  • record_type:表示当前记录的类型: | 0 | 1 | 2 | 3 | | —- | —- | —- | —- | | 普通记录 | B+树非叶节点的目录项记录 | Infimum 记录 | Supremum 记录 |

  • next_record:表示当前记录的真实数据到下一条记录的真实数据的距离。为正数:下条记录在当前记录后面;为负数:下条记录在当前记录前面。但是这里的下一条记录并不是插入顺序的,而是按照主键排列的。规定 Infimum 记录的下一条就是本页中主键最小的用户记录本页中主键最大的用户记录下一条就是 Supremum 记录

image.png

  • heap_no:向表中插入的记录一条接一条无间断的排列:image.png

这种排列结构为。把一条记录在堆中的相对位置称为 heap_no。每新申请一条记录的存储空间,该记录比物理位置在它前面的那条记录的 heap_no 值大1。

然而记录的 heap_no 值一般从2开始,因为 heap_no 为 0 和 1 两条记录是 InnoDB 自动向页中加入的。一条代表页面中的最小记录(Infimum)、一条代表页面中的最大记录(Supremum)。

对于一条完整的记录来说,比较记录的大小相当于比较主键的大小。无论向页中插入多少记录,规定任何用户记录都比 Infimum 大,比 Supremum 小

它们二者构造很简单,由 5 字节的记录头信息和 8 字节的一个固定单词组成:
image.png
由于这两条记录是InnoDB默认创建的,所以不放在存放页的 User Records 部分,而是单独放在一个称为Infimum+Supremum的部分

另外,堆中记录的 heap_no 值在分配后就不会变了,即使之后删除了堆中的记录,这条被删除记录的 heap_no 值也保持不变。

5.4 Page Directory(页目录)

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

InnoDB对分组有规定:对于Infimum记录所在的分组只能有1条记录Supremum记录所在的分组拥有的记录数只能在1~8间剩下的分组中的记录数只能在4~8间

在使用了页目录后,查找速度就提升很多了,查找过程分为两步:

  1. 通过二分法确定该记录所在分组对应的槽,然后找到该槽所在分组中主键值最小的那条记录(由于各个槽都是挨着的,所以可以轻松查到上一个槽的记录,这条记录的下一条就是要遍历的槽中最小的记录);
  2. 通过记录的 next_record 属性遍历该槽所在的组中各个记录。

5.5 Page Header(页面头部)

Page Head 固定占用56字节:
image.png

5.6 File Header(文件头部)

各种类型的页都会以 File Header 作为第一个部分,固定占用38个字节:
image.png
其中 FIL_PAGE_PREV 和 FIL_PAGE_NEXT:用于一张表的数据分散于多个页时,将这些页串起来。通过建立一个双向链表,而无需这些页在物理上真正连着:
image.png

5.7 File Trailer(文件尾部)

每个页的尾部有这一部分,由8字节组成,分为两个部分:

  • 前 4 个字节代表页的校验和。与 File Header 中的校验和相对应。每当一个页面在内存中发生修改时,在刷新到磁盘之前就要把页面的校验和计算出来。因为 File Header 在页面的前面,所以File Header 中的校验和会首先刷新到磁盘,当全部写完后,校验和也会被写到页尾部。这也就意味着,如果页面刷新成功,File Header 和 File Trailer 的校验和应该是一致的;而如果发生崩溃或断电,File Header 的校验和代表已修改的页,File Trailer 的校验和代表原来的页,二者不同意味着发生错误!

  • 后 4 字节表示页面最后修改时对应的 LSN 的后 4 字节。