页是InnoDB管理存储空间的基本单位,默认大小16KB。
InnoDB设计了多种不同类型的页。
页的类型 #
下表列出了InnoDB的数据页结构:
| 英文名称 | 中文名称 | 描述 |
|---|---|---|
| File Header | 文件头部 | 页的通用信息。 |
| Page Header | 页面头部 | 数据页转悠的一些信息。 |
| Infimum + Supremum | 最小记录和最大记录 | 虚拟记录 |
| User Records | 用户记录 | 用户存储的记录内容 |
| Free Space | 空闲空间 | 页中尚未使用的空间 |
| Page Directory | 页面目录 | 页中记录的相对位置 |
| File Trailer | 文件尾部 | 页的校验信息 |
一开始生成页的时候,并没有User Records部分,每当插入一条记录时,都会从Free Space中分配一块空间,并将这个空间划分到User Records中。
记录 #
记录头信息记录了该记录的一些基本信息,其中重要的有:
| 名称 | 描述 |
|---|---|
| deleted_flag | 标记该记录是否被删除 |
| min_rec_flag | 标记该记录是否是最小记录-B+树中每层非叶子节点中的最小的目录项记录都会添加该标记。 |
| heap_no | 记录在页中的位置 |
| next_record | 指向下一条记录的位置 |
| record_type | 记录类型,包括普通记录、最小记录、最大记录、B+树非叶子节点记录等。 |
任何用户记录都比Infimum记录大,任何用户记录都比Supremum记录小。
Supremum记录的next_record值为0,该记录之后就没有下一条记录,其是页中单向链表的最后一个节点。
页目录/Page Directory #
为了快速查找数据页中的记录,不必遍历整个链表,InnoDB引入了页目录:
- 将所有的正常的记录(包括最小记录和最大记录)划分为几个组。
- 每个组的最后一条记录相当于 “带头大哥”,其记录的头信息中的n_owned属性表示该组内共有几条记录。
- 将每个组中最后一条记录在页面中的地址偏移量提取出来,按照顺序存储到靠近页面尾部的地方。这个地方就是Page Directory。页目录中的这些地址偏移量成为槽(Slot)。
InnoDB对于每个分组中的记录条数是有规定的:对于Infimum记录所在的分组中只能有1条记录,Supremum记录所在的分组拥有的记录条数只能在1
8条之间,剩下的分组中记录的条数范围只能是在48条之间。每次插入的时候,从页目录中找到键值比待插入记录的键值大且差值最小的槽(即第一个比待插入记录键值大的槽)。
页面头部/Page Header #
Page Header记录了数据页中的记录的状态信息,比如存储了多少条记录、Free Space在页面中的地址偏移量、页目录中存储了多少个槽等。其中部分状态/字段为:
- PAGE_N_DIR_SLOTS 。 在页目录中的槽数量。
- PAGE_FREE。 各个已删除的记录通过next_record组成一个单向链表。PAGE_FREE表示改链表头节点对应记录在页面中的偏移量。
- PAGE_GARBAGE。已删除记录占用的字节数。
- PAGE_LEVEL。当前页在B+树中所处的层级。
- PAGE_MAX_TRX_ID。修改当前页的最大事务id,该值仅在二级索引页面中定义。
- PAGE_INDEX_ID。索引id,表示当前页属于哪个索引。
File Header #
File Header是各种类型的页通用部分,描述一些通用部分,是各种类型的页第一个组成部分。其内容组成如下:
- FIL_PAGE_SPACE_OR_CHECKSUM 页的校验和。
- FILE_PAGE_OFFSET 页号。
- FILE_PAGE_PREV 上一个页的页号。
- FILE_PAGE_NEXT 下一个页的页号。
- FILE_PAGE_LSN 页面被最后修改的LSN值。
- FILE_PAGE_TYPE 页的类型。
- FILE_PAGE_ARCH_LOG_NO_OR_SPACE_ID 页所属的表空间。
File Trailer #
File Trailer的作用主要是检测一个页是否完整,可以分成2部分:
- 前4字节代表页的校验和。如果页面刷新成功,则页首和页尾的校验和应该是一致的。 二者不同则意味着刷新期间发生了错误。
- 后4字节代表页面被最后修改时对应的LSN的后4字节。