【MySQL技术内幕】4.4-InnoDB数据页结构

1,404 阅读8分钟

欢迎大家关注 github.com/hsfxuebao/j… ,希望对大家有所帮助,要是觉得可以的话麻烦给点一下Star哈

转自:blog.csdn.net/shenchaohao…

相信通过前面几个小节的介绍,读者已经知道页是 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(文件结尾信息)

其中 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字节。

File header组成部分

名称

大小(字节)

说明

FIL_PAGE_SPACE_OR_CHKSUM

4

当 MySQL为 MySQL40.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( Log Sequence Number)

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开始,该值代表页属于哪个表空间

InnoDB存储引擎页的类型

名称

十六进制

解释

FIL_PAGE_INDEX

0x45BF

B+树叶节点

FIL_PAGE_UNDO_LOG

0x0002

Undo Log页

FIL_PAGE_INODE

0x0003

索引节点

FIL_PAGE_IBUF_FREE_LIST

0x0004

Insert Buffer空闲列表

FIL_PAGE_TYPE_ALLOCATED

0x0000

该页为最新分配

FIL_PAGE_IBUF_BITMAP

0x0005

Insert Buffer位图

FIL_PAGE_TYPE_SYS

0x0006

系统页

FIL_PAGE_TYPE_TRX_SYS

0x0007

事务系统数据

FIL_PAGE_TYPE_FSP_HDR

0x0008

File Space Header

FIL_PAGE_TYPE_XDES

0x0009

扩展描述页

FIL_PAGE_TYPE_BLOB

0x000A

BLOB页

2、Page Header

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

Page Header组成部分

名称

大小(字节)

说明

PAGE_N_DIR SLOTS

2

在 Page Directory(页目录)中的Slot(槽)数,“Page Directory”小节中会介绍

PAGE_HEAP_TOP

2

堆中第一个记录的指针,记录在页中是根据堆的形式存放的

PAGE_N_HEAP

2

堆中的记录数。一共占用2字节,但是第15位表示行记录格式

PAGE_FREE

2

指向可重用空间的首指针

PAGE_GARBAGE

2

已删除记录的字节数,即行记录结构中 delete flag为1的记录大小的总数

PAGE_LAST_INSERT

2

最后插入记录的位置

PAGE_DIRECTION

2

最后插入的方向。可能的取值为:
PAGE_LEFT(0x01)
PAGE_RIGHT(0x02)
PAGE_DIRECTION
PAGE_SAME_REO(0x03)
PAGE_SAME_PAGE(0x04)
PAGE_NO_DIRECTION (Ox05)

PAGE_N_DIRECTION

2

一个方向连续插人记录的数量

PAGE_NRECS

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页中定义

3、Infimum和Supremum Record

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

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来开启或关闭对这个页完整性的检查MySQL56.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数据库升级到 MySQL566或之后的版本,启用 strict crc32将导致不能读取表中的页。启用 strict crc32方式是最快的方式,因为其不再对 innodb和crc32算法进行两次检测。故推荐使用该设置。若数据库从低版本升级而来,则需要进行 mysql_upgrade操作。