文章目录
1 InnoDB 数据页结构
我们已经知道页是 InnoDB 存储引擎管理数据库的最小磁盘单位。页类型为 B-tree Node 的页存放的即是表中行的实际数据了。在这一节
中,我们将从底层具体地介绍 InnoDB 数据页的内部存储结构
InnoDB 数据页由以下7个部分组成:
- File Header (文件头)
- Page Header (页头)
- Infimun 和 Supremum Records
- User Records (用户记录,即行记录)
- Free Space (空闲空间)
- Page Directory (页目录)
- File Trailer (文件结尾信息)
其中 File Header
、 Page Header
、File Trailer
的大小是固定的,分别为 38、56、8 字节,这些空间用来标记该页的一些信息,如Checksum,数据页所在B+树索引的层数等。User Records、Free Space、Page Directory这些部分为实际的行记录存储空间,因此大小是动态的。在接下来的各小节中将具体分析各组成部分
1.1 File Header
File Header用来记录页的一些头信息,由下表中的8个部分组成,共占用38字节。
名称 | 大小(字节) | 说明 |
---|---|---|
FIL_PAGE_SPACE_OR_CHKSUM | 4 | 该值代表页的checksum值 |
FIL_PAGE_OFFSET | 4 | 表空间中页的偏移位。如某独立表空间a.ibd的大小为1GB,如果页的大小为16KB,那么总共有65536个页。FIL_PAGE_OFFSET表示该页在所有页中的位置。若此表空间的ID为10,那么搜索页(10,1)就表示查找表a中的第二个页 |
FIL_PAGE_PREV | 4 | 当前页的上一个页,B+ Tree特性决定了叶子节点必须是双向列表 |
FIL_PAGE_NEXT | 4 | 当前页的下一个页,B+Tree特性决定了叶子节点必须是双向列表 |
FIL_PAGE_LSN | 8 | 该值代表该页最后被修改的日志序列位置LSN (Log Sequence Number) |
FIL_PAGE_TYPE | 2 | InnoDB存储引擎页的类型,记住0x45BF,该值代表了存放的是数据页,即实际行记录的存储空间 |
FIL_PAGE_FILE_FLUSH_LSN | 8 | 该值仅在系统表空间的一个页中定义 |
FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID | 4 | 该值代表页属于哪个表空间 |
以从某 ibd 文件捞出来的 File Header 举例如下:
// 用[]框出来范围
3073 0000c000 [87 b8 54 b1 00 00 00 03 ff ff ff ff ff ff ff ff |..T.............|
3074 0000c010 00 00 00 00 00 2d 8e d7 45 bf 00 00 00 00 00 00 |.....-..E.......|
3075 0000c020 00 00 00 00 00 27] 00 1a 0d c0 80 66 00 00 00 00 |.....'.....f....|
-
FIL_PAGE_SPACE_OR_CHKSUM
87 b8 54 b1
-
FIL_PAGE_OFFSE
00 00 00 03
偏移量是3说明是表空间的低4页
-
FIL_PAGE_PREV
ff ff ff ff
-
FIL_PAGE_NEXT
ff ff ff ff
-
FIL_PAGE_LSN
00 00 00 00 00 2d 8e d7
-
FIL_PAGE_TYPE
45 bf
表明是数据页
-
FIL_PAGE_FILE_FLUSH_LSN
00 00 00 00 00 00 00 00
-
FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID
00 00 00 27
2.2 Page Header
接着 File Header 部分的是 Page Header,该部分用来记录数据页的状态信息,由14个部分组成,共占用56字节,如下表所示
名称 | 大小(字节) | 说明 |
---|---|---|
PAGE_N_DIR_SLOTS | 2 | 在 Page Directory (页目录)中的 Slot (槽)数。后文重点介绍 |
PAGE_HEAP_TOP | 2 | 堆中第一个记录的指针,记录在页中是根据堆的形式存放的 |
PAGE_N_HEAP | 2 | 堆中的记录数。一共占用2字节,但是第15位指示行记录格式 |
PAGE_FREE | 2 | 指向可重用空间的首指针 |
PAGE_GARBAGE | 2 | 已删除记录的字节数,即行记录结构中delete flag为1的记录大小的总数 |
PAGE_LAST_INSERT | 2 | 最后插入记录的位置 |
PAGE_DJRECTION | 2 | 最后插入的方向。 |
PAGE_N_DIRECTION | 2 | 一个方向连续插入记录的数量 |
PAGE_N_RECS | 2 | 该页中记录的数量 |
PAGE_MAX_TRX_ID | 8 | 修改当前页的最大事务ID,注意该值仅在Secondary Index中定义 |
PAGE_LEVEL | 2 | 当前页在索引树中的位置,0x00代表叶节点,即叶节点总是在第0层 |
PAGE_INDEX_ID | 8 | 索引ID,表示当前页属于哪个索引 |
PAGE_BTR_SEG_LEAF | 10 | B+树数据页非叶节点所在段的segment header。注意该值仅在B+树的Root页中定义 |
PAGE_BTR_SEG_TOP | 10 | B+树数据页所在段的segment header。注意该值仅在B+树的Root页中定义 |
2.3 Infimum 和 Supremum Record
这个记录还挺重要的
在InnoDB存储引擎中,每个数据页中有两个虚拟的行记录,用来限定记录的边界。Infimum记录是比该页中任何主键值都要小的值,Supremum指比任何可能大的值还要大的值。这两个值在页创建时被建立,并且在任何情况下不会被删除。在Compact行格式和Redundant行格式下,两者占用的字节数各不相同
2.4 User Record 和 Free Space
User Record就是之前讨论过的部分,即实际存储行记录的内容。再次强调,InnoDB存储引擎表总是B+树索引组织的。Free Space很明显指的就是空闲空间,同样也是个链表数据结构。在一条记录被删除后,该空间会被加入到空闲链表中。
2.5 Page Directory(InnoDB 数据页结构最重要的部分)
Page Directory (页目录)中存放了记录的相对位置(注意,这里存放的是页相对位置,而不是偏移量),
在InnoDB中并不是每个记录拥有一个槽,InnoDB存储引擎的槽是一个稀疏目录(sparse directory),即一个槽中可能包含多个记录。伪记录Intimum的n owned值总是为1,记录Supremum的n owned的取值范围为[1, 8 ],其他用户记录n_owned的取值范围为[4, 8]。当记录被插入或删除时需要对槽进行分裂或平衡的维护操作。
在Slots中记录按照索引键值顺序存放,这样可以利用二叉查找迅速找到记录的指针。假设有(‘i’, ‘d’, ‘c’, ‘b’, ‘e’, ‘g’, ‘l’. ‘h’, ‘f’, ‘j’, ‘k’, ‘a’),同时假设一个槽中包含4条记录,则Slots中的记录可能是:(‘a’, ‘e’, ‘i’)。
由于在InnoDB存储引擎中Page Direcotry是稀疏目录,二叉查找的结果只是一个粗略的结果,因此InnoDB存储引擎必须通过recorder header中的next record来继续查找,相关记录。同时,Page Directory很好地解释了 recorder header中的n owned值的含义, 因为这些记录并不包括在Page Directory中。
这里的 Page Directory 与 InnoDB 行记录中的 record header 联系了起来
如何查找一条记录:
1、先在B+数中找到记录所在的页
2、把页加载入内存
3、通过 Page Directory 进行二叉查找。因为 Page Directory 是稀疏的所有只能找到大概位置。
4、在步骤3的基础上继续根据记录偏移量一行一行的查找
因为步骤3,4都是在内存中的操作,且步骤3是二分查找法,因此通常忽略这部分所消耗的时间
2.6 File Trailer
为了检测页是否已经完整地写入磁盘(如可能发生的写入过程中磁盘损坏、机器关机等),InnoDB存储引擎的页中设置了 File Trailer部分。
File Trailer只有一个FIL_PAGE_END_LSN部分,占用8字节。前4字节代表该页的checksum值,最后4字节和File Header中的FIL PAGE LSN相同。将这两个值与File Header 中的 FIL_PAGE_SPACE_OR_CHKSUM 和 FIL_PAGE_LSN 值进行比较,看 是否一致(checksum的比较需要通过InnoDB的checksum函数来进行比较,不是简单的等值比较),以此来保证页的完整性(not corrupted)
2 参考资料
书籍:《InnoDB 存储引擎》,该书电子版书籍作者无套路免费下载
InnoDB 数据页结构(实验):我的文章:《15.6.1 InnoDB 数据页格式(数据页实验,重要).md》
传送门: 保姆式Spring5源码解析
欢迎与作者一起交流技术和工作生活