5.2 数据页结构快览
一个InnoDB数据页的存储空间大致被划分为7个部分
名称 | 中文名 | 大小(字节) | 描述 |
---|---|---|---|
File Header | 文件头部 | 38 | 页的一些通用信息 |
Page Header | 页面头部 | 56 | 数据页专有的一些信息 |
Infimum+Supremum | 最小记录和最大记录 | 26 | 两个虚拟记录 |
User Records | 用户记录 | 不确定 | 用户存储的记录内容 |
Free Space | 空闲空间 | 不确定 | 页中尚未使用的空间 |
Page Directory | 页目录 | 不确定 | 页中某些记录的相对位置 |
File Trailer | 文件尾部 | 8 | 校验页是否完整 |
5.3 记录在页中的存储
记录头信息的含义
deleted_flag
:标记当前记录是否被删除,值为0时表示记录没有被删除,值为1时表示记录被删除了。所有被删除的记录会形成一个垃圾链表,记录在这个链表中占用的空间称为可重用空间,之后若有新记录插入到表中,就可以覆盖可重用空间heap_no
:把记录一条一条排列的结构称为堆,把一条记录在堆中的相对位置称为heap_no
。堆中记录的heap_no
值在分配之后就不会发生变动了,即使之后删除了堆中的某条记录,这条被删除记录的heap_no
值也保持不变
页中自动加了两条记录:一条代表页面中的最小记录(Infimum),一条代表页面中的最大记录(Supremum),它们在页面中的位置最靠前
- next_record:表示从当前记录的真实数据到下一条记录的真实数据的距离。如果是正数,说明下一条记录在后面,负数则表示下一条记录在前面,是按照主键值从小到大串联的
指针指向真实数据的用意:向左读取就是记录头信息,向右读取就是真实数据,头信息中变长字段长度列表,NULL值列表是逆序存放的,这样可以使记录中位置靠前的字段和它们对应的字段长度信息在内存中的距离更近,可能会提高缓存命中率
5.4 Page Directory(页目录)
在页中快速检索一条记录是通过页目录实现的
- 将所有未删除的记录(包括
Infimum
和Supremum
记录)划分为几个组 - 每个组的最后一条记录的头信息中
n_owned
属性表示该组内有几条记录 - 将每个组的最后一条记录在页面中的地址偏移量(就是该记录的真实数据与页面中第0字节之间的距离)单独提取出来,按顺序存储到
Page Directory
,这些地址偏移量称为槽(slot),每个槽占用2字节
- Infimum记录所在的分组只能有1条记录
- Supremum记录所在的分组可以有1~8条记录
- 其他的分组的记录条数在4~8之间
在一个数据页中查找指定主键值的记录时,分为两步
- 通过二分法确定该记录所在分组对应的槽,然后找到该槽所在分组中主键值最小的那条记录
- 通过记录的next_record属性遍历该槽所在的分组的各个记录
5.5 Page Header(页面头部)
描述存储在数据页中的记录的状态信息,占用56字节
名称(PAGE_) | 大小(字节) | 描述 |
---|---|---|
N_DIR_SLOTS | 2 | 在页目录中的槽数量 |
HEAP_TOP | 2 | 还未使用的空间的最小地址,该地址之后就是Free Space |
N_HEAP | 2 | 第1位表示是否为紧凑型记录,剩余15位表示堆中记录的数量(包括Infimum、Supremum以及标记为删除的记录) |
FREE | 2 | 以删除的记录会形成一条单向链表,用来表示该链表头结点对应记录在页面中的偏移量 |
GARAGE | 2 | 已删除记录占用的字节数 |
LAST_INSERT | 2 | 最后插入记录的位置 |
DIRECTION | 2 | 记录插入的方向 |
N_DIRECTION | 2 | 一个方向连续插入的记录数量 |
N_RECS | 2 | 该页中用户记录的数量(不包括Infimum、Supremum以及标记为删除的记录) |
MAX_TRX_ID | 8 | 修改当前页的最大事务id,该值仅在二级索引页面中定义 |
LEVEL | 2 | 当前页在B+树中所处的层级 |
INDEX_ID | 8 | 索引id,表示当前页属于哪个索引 |
BTR_SEG_LEAF | 10 | B+树叶子节点段的头部信息,仅在B+树根页面中定义 |
BTR_SEG_TOP | 10 | B+树非叶子节点段的头部信息,仅在B+树根页面中定义 |
Page_DIRECTION
:加入新插入的一条记录的主键值比上一条记录的主键值大,我们说这条记录的插入方向是右边,反之则是左边Page_N_DIRECTION
:假设连续几次插入新记录的方向都是一致的,InnoDB会把沿着同一个方向插入记录的条数记下来
5.6 File Header(文件头部)
各种类型的页都会以File Header作为第一个组成部分,描述了一些通用于各种页的信息,固定38字节
名称(FIL_PAGE_) | 大小(字节) | 描述 |
---|---|---|
SPACE_OR_CHECKSUM | 4 | 页的校验和 |
OFFSET | 4 | 页号 |
PREV | 4 | 上一个页的页号 |
NEXT | 4 | 下一个页的页号 |
LSN | 8 | 页面最后修改时对应的LSN(Log Sequence Number,日志序列号)值 |
TYPE | 2 | 该页的类型 |
FILE_FLUSH_LSN | 8 | 仅在系统表空间的第一个页中定义,代表文件至少被刷新到了对应的LSN值 |
ARCH_LOG_NO_OR_SPACE_ID | 4 | 页属于哪个表空间 |
存储记录的数据页可以组成一个双向链表
5.7 File Trailer(文件尾部)
该部分由8字节组成,分为2部分
- 前4字节代表页的校验和,和文件头部的校验和相对应,如果页面成功刷新到磁盘,则页首和页尾的校验和应该是一致的
- 后4字节是页面最后被修改时的
LSN
的后4字节,正常应该跟页面头部的FIL_PAGE_LSN
的后4字节相同