5.2 数据页结构快览
其中各部分的作用:
5.3 记录在页中的存储
我们自己存储的记录会按照指定的行格式存储到 User Records 部分。一开始生成页的时候没有 User Records 部分,每当插入一条记录都会从 Free Space 部分申请一个记录大小的空间划分到User Records 部分。
当 Free Space 部分空间全部被 User Records 部分替代后,就意味着这个页用完了;如果还有新记录插入,就要另外申请页面了:
记录头信息
其图形化呈现如下:
deleted_flag:用来标记当前记录是否被删除。为 0 表示没有被删除,为 1 表示被删除了。其实被删除的记录仍然还在磁盘上,因为在移除它们后,还需要在磁盘上重新排列其他的记录,这会带来性能消耗,所以使用懒惰删除,打一个删除标记即可。所有被删除的记录会组成一个垃圾链表,记录该链表中占用的空间称为可重用空间。之后若有新记录插入表,可能使用可重用空间的内存。
min_rec_flag:B+树的每层非叶子节点中的最小的目录项记录都会添加到该标记。
n_owned:(在后面介绍)
record_type:表示当前记录的类型: | 0 | 1 | 2 | 3 | | —- | —- | —- | —- | | 普通记录 | B+树非叶节点的目录项记录 | Infimum 记录 | Supremum 记录 |
- next_record:表示当前记录的真实数据到下一条记录的真实数据的距离。为正数:下条记录在当前记录后面;为负数:下条记录在当前记录前面。但是这里的下一条记录并不是插入顺序的,而是按照主键排列的。规定 Infimum 记录的下一条就是本页中主键最小的用户记录;本页中主键最大的用户记录下一条就是 Supremum 记录。
- heap_no:向表中插入的记录一条接一条无间断的排列:
这种排列结构为堆。把一条记录在堆中的相对位置称为 heap_no。每新申请一条记录的存储空间,该记录比物理位置在它前面的那条记录的 heap_no 值大1。
然而记录的 heap_no 值一般从2开始,因为 heap_no 为 0 和 1 两条记录是 InnoDB 自动向页中加入的。一条代表页面中的最小记录(Infimum)、一条代表页面中的最大记录(Supremum)。
对于一条完整的记录来说,比较记录的大小相当于比较主键的大小。无论向页中插入多少记录,规定任何用户记录都比 Infimum 大,比 Supremum 小。
它们二者构造很简单,由 5 字节的记录头信息和 8 字节的一个固定单词组成:
由于这两条记录是InnoDB默认创建的,所以不放在存放页的 User Records 部分,而是单独放在一个称为Infimum+Supremum的部分。
另外,堆中记录的 heap_no 值在分配后就不会变了,即使之后删除了堆中的记录,这条被删除记录的 heap_no 值也保持不变。
5.4 Page Directory(页目录)
- 将所有正常记录(包括 Infimum 和 Supremum 记录,但不包括移除到垃圾链表的记录)划分为几组;
- 每个组最后一条记录(组内最大的那条记录)的头信息中的 n_owned 属性表示该组内共有几条记录;
- 将每个组中最后一条记录在页面中的地址偏移量(即该记录的真实数据与页面中第0个字节之间的距离)单独提取出来,按顺序存储到靠近页尾部的地方——Page Directory(页目录)。页目录中的这些地址偏移量是槽,每个槽占用2字节。
InnoDB对分组有规定:对于Infimum记录所在的分组只能有1条记录,Supremum记录所在的分组拥有的记录数只能在1~8间,剩下的分组中的记录数只能在4~8间。
在使用了页目录后,查找速度就提升很多了,查找过程分为两步:
- 通过二分法确定该记录所在分组对应的槽,然后找到该槽所在分组中主键值最小的那条记录(由于各个槽都是挨着的,所以可以轻松查到上一个槽的记录,这条记录的下一条就是要遍历的槽中最小的记录);
- 通过记录的 next_record 属性遍历该槽所在的组中各个记录。
5.5 Page Header(页面头部)
Page Head 固定占用56字节:
5.6 File Header(文件头部)
各种类型的页都会以 File Header 作为第一个部分,固定占用38个字节:
其中 FIL_PAGE_PREV 和 FIL_PAGE_NEXT:用于一张表的数据分散于多个页时,将这些页串起来。通过建立一个双向链表,而无需这些页在物理上真正连着:
5.7 File Trailer(文件尾部)
每个页的尾部有这一部分,由8字节组成,分为两个部分:
前 4 个字节代表页的校验和。与 File Header 中的校验和相对应。每当一个页面在内存中发生修改时,在刷新到磁盘之前就要把页面的校验和计算出来。因为 File Header 在页面的前面,所以File Header 中的校验和会首先刷新到磁盘,当全部写完后,校验和也会被写到页尾部。这也就意味着,如果页面刷新成功,File Header 和 File Trailer 的校验和应该是一致的;而如果发生崩溃或断电,File Header 的校验和代表已修改的页,File Trailer 的校验和代表原来的页,二者不同意味着发生错误!
后 4 字节表示页面最后修改时对应的 LSN 的后 4 字节。