2023-03-23
原文作者:一直不懂 原文地址:https://blog.csdn.net/shenchaohao12321/article/details/82798275

相信通过前面几个小节的介绍,读者已经知道页是 InnoDB存储引擎管理数据库的最小磁盘单位。页类型为B-Tree Node的页存放的即是表中行的实际数据了。在这一节中,我们将从底层具体地介绍 InnoDB数据页的内部存储结构。注意 InnoDB公司本身并没有详细介绍其页结构的实现, MySQL的官方手册中也基本没有提及 InnoDB存储引擎页的内部结构。
InnoDB数据页由以下7个部分组成,如图所示。

  • File header(文件头)
  • Page Header(页头)
  • Infimum和 Supremum Records
  • User records(用户记录,即行记录)
  • Free Space(空闲空间)
  • Page Directory(页目录)
  • File Trailer(文件结尾信息)

202303232331302321.png

其中 File header、 Page Header、 File trailer的大小是固定的,分别为38、56、8字节,这些空间用来标记该页的一些信息,如 Checksum,数据页所在B+树索引的层数等。 User Records、 Free Space、 Page Directory这些部分为实际的行记录存储空间,因此大小是动态的。

1、File Header

File header用来记录页的一些头信息,由表4-3中8个部分组成,共占用38字节。

名称 大小(字节) 说明
名称 大小(字节) 说明
FIL_PAGE_SPACE_OR_CHKSUM 4 当MySQL为MySQL4.0.14之前的版本时,该值为0。在之后的MySQL版本中,该值代表页的checksum值(一种新的checksum值)
FIL_PAGE_OFFSET 4 表空间中页的偏移值。如某独立表空间a.ibd的大小为1GB,如果页的FIL_PAGE_OFFSET大小为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(LogSequenceNumber)
FIL_PAGE_TYPE 2 InnoDB存储引擎页的类型。常见的类型见下表。记住0x45BF,该值代表了存放的是数据页,即实际行记录的存储空间
LSN_FLUSH_LSN 8 该值仅在系统表空间的一个页中定义,代表文件至少被更新到了该LSN_FLUSH_LSN值。对于独立表空间,该值都为0
FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID 4 从MySQL4.1开始,该值代表页属于哪个表空间
名称 十六进制 解释
名称 十六进制 解释
FIL_PAGE_INDEX 0x45BF B+树叶节点
FIL_PAGE_UNDO_LOG 0x0002 UndoLog页
FIL_PAGE_INODE 0x0003 索引节点
FIL_PAGE_IBUF_FREE_LIST 0x0004 InsertBuffer空闲列表
FIL_PAGE_TYPE_ALLOCATED 0x0000 该页为最新分配
FIL_PAGE_IBUF_BITMAP 0x0005 InsertBuffer位图
FIL_PAGE_TYPE_SYS 0x0006 系统页
FIL_PAGE_TYPE_TRX_SYS 0x0007 事务系统数据
FIL_PAGE_TYPE_FSP_HDR 0x0008 FileSpaceHeader
FIL_PAGE_TYPE_XDES 0x0009 扩展描述页
FIL_PAGE_TYPE_BLOB 0x000A BLOB页

2、Page Header

接着 File header部分的是 Page Header,该部分用来记录数据页的状态信息,由14个部分组成,共占用56字节,如下表所示。

名称 大小(字节) 说明
名称 大小(字节) 说明
PAGE_N_DIR_SLOTS 2 在PageDirectory(页目录)中的Slot(槽)数,“PageDirectory”小节中会介绍
PAGE_HEAP_TOP 2 堆中第一个记录的指针,记录在页中是根据堆的形式存放的
PAGE_N_HEAP 2 堆中的记录数。一共占用2字节,但是第15位表示行记录格式
PAGE_FREE 2 指向可重用空间的首指针
PAGE_GARBAGE 2 已删除记录的字节数,即行记录结构中deleteflag为1的记录大小的总数
PAGE_LAST_INSERT 2 最后插入记录的位置
PAGE_DIRECTION 2 最后插入的方向。可能的取值为:PAGE_LEFT(0x01)PAGE_RIGHT(0x02)PAGE_DIRECTIONPAGE_SAME_REO(0x03)PAGE_SAME_PAGE(0x04)PAGE_NO_DIRECTION(Ox05)
PAGE_N_DIRECTION 2 一个方向连续插人记录的数量
PAGE_NRECS 2 该页中记录的数量
PAGE_MAX_TRX_ID 8 修改当前页的最大事务ID,注意该值仅在SecondaryIndex中定义
PAGE_LEVEL 2 当前页在索引树中的位置,0x00代表叶节点,即叶节点总是在第0层
PAGE_INDEX_ID 8 索引ID,表示当前页属于哪个索引
PAGE_BTR_SEG_LEAF 10 B+树数据页非叶节点所在段的segmentheader。注意该值仅在B+树的Root页中定义
PAGE_BTR_SEG_TOP 10 B+树数据页所在段的segmentheader。注意该值仅在B+树的Root页中定义

3、Infimum和Supremum Record

在 InnoDB存储引擎中,每个数据页中有两个虚拟的行记录,用来限定记录的边界。 Infimum记录是比该页中任何主键值都要小的值, Supremum指比任何可能大的值还要大的值。这两个值在页创建时被建立,并且在任何情况下不会被删除。在 Compact行格式和 Redundant行格式下,两者占用的字节数各不相同。下图显示了 Infimum和Supremum记录。

202303232331318412.png

4、User Record 和 Free Space

User Record就是之前讨论过的部分,即实际存储行记录的内容。再次强调, InnoDB存储引擎表总是B+树索引组织的。
Free Space很明显指的就是空闲空间,同样也是个链表数据结构。在一条记录被删除后,该空间会被加入到空闲链表中。

5、Page Directory

Page Directory(页目录)中存放了记录的相对位置(注意,这里存放的是页相对位置,而不是偏移量),有些时候这些记录指针称为Slots(槽)或目录槽(Directory Slots)。与其他数据库系统不同的是,在 InnoDB中并不是每个记录拥有一个槽, InnoDB存储引擎的槽是一个稀疏目录(sparse directory),即一个槽中可能包含多个记录。伪记录 Infimum的 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中。
需要牢记的是,B+树索引本身并不能找到具体的一条记录,能找到只是该记录所在的页。数据库把页载入到内存,然后通过Page Directory再进行二叉查找。只不过二叉査找的时间复杂度很低,同时在内存中的查找很快,因此通常忽略这部分查找所用的时间。

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)。
在默认配置下, InnoDB存储引擎每次从磁盘读取一个页就会检测该页的完整性,即页是否发生 Corrupt,这就是通过 File trailer部分进行检测,而该部分的检测会有一定的开销。用户可以通过参数 innodb checksums来开启或关闭对这个页完整性的检查MySQL5.6.6版本开始新增了参数 innodb_checksum_algorithm,该参数用来控制检测 checksum函数的算法,默认值为crc32,可设置的值有: innodb、crc32、none、 strict_innodb、 strict_crc32、 strict_none。
innodb为兼容之前版本 InnoDB页的 checksum检测方式,crc32为 MySQL5.6.6版本引进的新的 checksum算法,该算法较之前的 innodb有着较高的性能。但是若表中所有页的 checksum值都以 strict算法保存,那么低版本的 MySQL数据库将不能读取这些页。none表示不对页启用 checksum检查strict*,正如其名,表示严格地按照设置的 checksum算法进行页的检测。因此若低版本 MySQL数据库升级到 MySQL5.6.6或之后的版本,启用 strict_crc32将导致不能读取表中的页。启用 strict_crc32方式是最快的方式,因为其不再对 innodb和crc32算法进行两次检测。故推荐使用该设置。若数据库从低版本升级而来,则需要进行 mysql_upgrade操作。

阅读全文