InnoDB页面结构

InnoDB所有数据文件(ibdata以及ibd)都由页组成。页在内核实现中称为page或block,page偏向于指向物理页面,block偏向于指向page被加载到内存后,用来管理page的内存结构。在未被压缩情况下,一个页的大小为UNIV_PAGE_SIZE(16384,16K)。不同用途的页具有相同格式的文件头和文件尾,其中记录了页面校验值、页面编号、表空间编号、LSN等通用信息。页面的组织方式在《InnoDB物理文件结构》中已有介绍,本文将深入InnoDB页面结构。

1 页面类型

以MySQL-8.0.30为例,所有页面类型如下:

类型 含义
FIL_PAGE_INDEX B+树节点
FIL_PAGE_RTREE R-树节点,R-tree是专门用来表示空间数据类型
FIL_PAGE_SDI SDI是冗余备份的表元数据信息,同样以B+树存储,此为SDI页面类型
FIL_PAGE_TYPE_UNUSED 目前此类型暂未使用
FIL_PAGE_UNDO_LOG 存储undo log的回滚段页面
FIL_PAGE_INODE 用于管理数据文件中的segment,每个inode页可以存储FSP_SEG_INODES_PER_PAGE(默认为85)个记录
FIL_PAGE_IBUF_FREE_LIST change buffer的空闲链表,change buffer介绍详见《Buffer Pool详解》
FIL_PAGE_TYPE_ALLOCATED 新分配的页面
FIL_PAGE_IBUF_BITMAP page_no为1、1+16384*N的页面都是FIL_PAGE_IBUF_BITMAP类型,用于记录其后16384个页面change buffer的信息
FIL_PAGE_TYPE_SYS 系统页
FIL_PAGE_TYPE_FSP_HDR page no 0/16384*N的页面都是extent描述页,page no 0还记录了与该table space相关的信息(FSP HEADER),类型为FIL_PAGE_TYPE_FSP_HDR
FIL_PAGE_TYPE_TRX_SYS 事务系统数据
FIL_PAGE_TYPE_XDES 除page no为0的页外所有extent描述页的类型,page no为16384*N
FIL_PAGE_TYPE_BLOB 解压的BLOB页面
FIL_PAGE_TYPE_ZBLOB 第一个压缩的BLOB页面
FIL_PAGE_TYPE_ZBLOB2 后续压缩的 BLOB 页面
FIL_PAGE_TYPE_UNKNOWN 在旧表空间中,FIL_PAGE_TYPE在刷盘时会临时替换为该值
FIL_PAGE_COMPRESSED 压缩页面
FIL_PAGE_ENCRYPTED 加密页面
FIL_PAGE_COMPRESSED_AND_ENCRYPTED 压缩和加密页面
FIL_PAGE_ENCRYPTED_RTREE 加密的 R-tree 页面
FIL_PAGE_SDI_BLOB 未压缩的 SDI BLOB 页面
FIL_PAGE_SDI_ZBLOB 压缩后的 SDI BLOB 页面
FIL_PAGE_TYPE_LEGACY_DBLWR 旧的double write buffer页面
FIL_PAGE_TYPE_RSEG_ARRAY 回滚段数组页面
FIL_PAGE_TYPE_LOB_INDEX 未压缩 LOB 的索引页
FIL_PAGE_TYPE_LOB_DATA 未压缩 LOB 的数据页
FIL_PAGE_TYPE_LOB_FIRST 未压缩 LOB 的第一页
FIL_PAGE_TYPE_ZLOB_FIRST 压缩 LOB 的第一页
FIL_PAGE_TYPE_ZLOB_DATA 压缩 LOB 的数据页
FIL_PAGE_TYPE_ZLOB_INDEX 压缩 LOB 的索引页。 此页面包含一个 z_index_entry_t 对象数组。
FIL_PAGE_TYPE_ZLOB_FRAG 压缩 LOB 的片段页面
FIL_PAGE_TYPE_ZLOB_FRAG_ENTRY 片段页的索引页(压缩的 LOB)

2 通用页面结构

任何页面都有统一的文件头和文件尾结构,用于记录页面的checksum校验、页面类型,用于维护逻辑页面链表的前后逻辑页面编号等。详细结构如下:

image.png

2.1 Fil Header

名称 大小 内容
FIL_PAGE_SPACE_OR_CHKSUM 4 MySQL4.0之前为space id,之后为CHECKSUM
FIL_PAGE_OFFSET 4 页码,每个表空间从0开始计数。页码乘以页面大小便是当前页面在数据文件中的偏移
FIL_PAGE_PREV 4 指向B+树同一层的前一个页面,第一个页面的FIL_PAGE_PREV为FIL_NULL
FIL_PAGE_NEXT 4 指向B+树同一层的下一个页面,最后一个页面的FIL_PAGE_NEXT为FIL_NULL
FIL_PAGE_LSN 8 LSN是一个一直递增的整型数字,表示事务写入到日志的字节总量,可以唯一区分对数据页的修改,此处记录页面最后一次修改的LSN
FIL_PAGE_TYPE 2 页面的类型
FIL_PAGE_FILE_FLUSH_LSN 8 两种作用:1)在系统表空间的第一个页中,记录MySQL关闭时checkpoint到的点,即刷入磁盘的页面LSN至少在该值之上;2)只在FIL_PAGE_COMPRESSED类型的数据页中被用于记录压缩信息
FIL_PAGE_SPACE_ID 4 索引页所在的表空间的ID

2.2 Fil Trailer

文件尾的作用是校验文件是否损坏,在每个页面的结尾的8个字节中,分别存储了checksum和LSN的后四位,对应于Fil Header中FIL_PAGE_LSN的内容。

3 索引页结构

InnoDB的用户表数据存储于FIL_PAGE_INDEX(通常称为索引页)类型的页面中,是InnoDB最重要的页面类型之一。下面介绍其页面结构:

image.png

3.1 Record

为了更好地理解Page Header和Page Directory。先介绍InnoDB索引页中记录的排列方式。记录的格式可以按如下格式理解。

变长字段长度 NULL 标志位 记录头信息 系统列 Field 1 ... Field N

其中记录头信息中包含了next_record、heap_no、delete_flag、n_owned等重要信息。下面依次介绍:

  • next_record:所有记录在页面中从前往后插入。假设id为primary key,id为1,3,2的三条记录依次插入同一页面,那么在页面中三条记录的排列顺序也为1,3,2。三条记录通过next_record属性相连,即id为1的记录的next_record指向id为2的记录,id为2的记录next_record指向id为3的记录。
  • heap_no:记录在页面中的物理编号,上述id为1,3,2的三条记录的heap_no依次为N,N+1,N+2。
  • delete_flag:记录被删除后并不是直接在页面中抹去,而是标记上delete_flag。这样做的目的是为了多版本并发控制服务(MVCC)。页面中被delete mark的记录也会通过next_record串联成链表,记录在Page Header中。当记录不再被MVCC需要时,会由purge线程从页面中彻底抹去。
  • n_owned:本属性与Page Directory相关,在Page Directory小节中介绍。

每个索引页为了方便对页内记录的访问,都添加了两条系统记录,它们没有变长字段列表和NULL值列表,只包含记录头信息和真实数据:infimum和supremum,分别代表虚拟的最小记录和虚拟最大记录。这两条记录固定在Page Header之后,分别是heap_no为0和heap_no为1的记录。从虚拟记录infimum开始一直通过next_record指针访问到虚拟记录supremum,可以按逻辑大小遍历页内的所有记录。

3.2 Page Directory

InnoDB B+树记录查询只能查询到数据页级别。假设一条记录的大小50字节,一个16384字节的页面可以存储300条记录左右,如果页内查询如果都通过next_record从infimum遍历到supremum,那么查询将非常低效。

Page Directory如其名,作为数据目录,是用来加速页内记录查找的。其由一个个slot组成,每个slot由两个字节组成,每个Slot指向一条记录,Slot的值是记录在页面内的偏移。每个Slot管理4~8条记录。被指向的记录作为组长记录,管理位于其之前的4-8条记录。组长记录的n_owned为其管理的记录数量。

image.png

如图所示,Rec5管理Rec1-Rec5,n_owned为5;Rec10管理Rec6-Rec10,n_owned为5;Rec11管理Rec11-Rec14,n_owned为4;Rec18管理Rec15-Rec18,n_owned为4。

一个page至少有两个Slot,第一个slot指向infimum记录,最后一个Slot指向supremum记录。Slot分配是从页面的最后倒数8个字节的Fil Trailer开始逆序分配的,所以严格意义上上图的Slot1-Slot4应该逆序。

在查找时首先数据目录上对Slot进行二分查找,定位到具体的slot后,然后在Slot内进行顺序查找。

3.3 Free Space

这部分是介于用户记录和Page Directory之间的一块连续的未被使用的内存。用户记录从前往后增长,Page Directory从后往前增长。在空间足够时,会直接从这里分配内存,当空间不足时,会重新整理页面内的记录,将碎片空间进行合并,或者分裂页面,具体行为取决于InnoDB各场景下的具体策略。在将空间分配给记录后,会递增PAGE_N_RECS和PAGE_N_HEAP的值。

3.4 Page Header

有了前三小节的介绍,下面对Page Header进行介绍。Page Header主要索引页内的统计信息,包含14部分,如下所示:

名称 大小 含义
PAGE_N_DIR_SLOTS 2 Page directory中的Slot个数
PAGE_HEAP_TOP 2 空余空间的起始地址在页内的偏移,如有新数据将从此位置插入
PAGE_N_HEAP 2 页面内所有的记录数:系统记录(Infimum和Supremum记录)、用户记录、标记被删除的记录。标记删除记录并不会减少此值。
PAGE_FREE 2 指向被标记删除的记录链表的第一个记录。通过此记录可以访问所有标记删除的记录
PAGE_GARBAGE 2 被标记删除的所有记录占用的总字节数,即可回收的空间大小
PAGE_LAST_INSERT 2 指向最近一次被插入记录的偏移量
PAGE_DIRECTION 2 最近一次记录插入的方向(从左或从右插入),每次插入时与PAGE_LAST_INSERT的记录进行比较,以确认插入方向
PAGE_N_DIRECTION 2 当前以相同方向顺序插入记录的个数
PAGE_N_RECS 2 页面上有效的用户记录的个数(不包括最小和最大记录以及被标记为删除的记录)
PAGE_MAX_TRX_ID 8 修改当前页面的最大事务ID,主要用于辅助判断二级索引记录的可见性。
PAGE_LEVEL 2 当前页面在B+树中所在的层数,叶子结点的值为0
PAGE_INDEX_ID 8 索引ID,表示当前页属于哪个索引
PAGE_BTR_SEG_LEAF 10 仅仅在B+树root页定义,叶子节点段在inode page中的位置
PAGE_BTR_SEG_TOP 10 仅仅在B+树root页定义,非叶子节点段在inode page中的位置

4 总结

本文在《InnoDB物理文件结构》的基础上进一步介绍了InnoDB的页面结构。首先介绍了所有InnoDB的页面类型,接着介绍了InnoDB页面的通用页面结构Fil Header和Fil Trailer,最后从Record、Page Directory、Free Space和Page Header四个方面介绍了InnoDB索引页的结构。

你可能感兴趣的:(InnoDB页面结构)