第 5 章 盛放记录的大盒子——InnoDB数据页结构

37 阅读9分钟

5.1 不同类型的页简介

InnoDB为了不同的目的设计了许多不同类型的页。

5.2 数据页结构快览

在这里插入图片描述

名称中文名占用空间大小简单描述
File Header文件头部38字节页的一些通用信息
Page Header页面头部56字节数据而专有的一些信息
Infimum + Supremum最小最大记录26字节两个虚拟的行记录
User Records用户记录不确定实际存在的行记录内容
Free Space空闲空间不确定页中尚未使用的空间
Page Directory页面目录不确定页中某些记录的相对位置
File Trailer文件尾部8字节校验页是否完整

5.3 记录在页中的存储

在这里插入图片描述

初始阶段,没有User Record这个部分,每当我们插入一条记录,都会从Free Space部分,申请一个记录大小的空间划分到User Records部分,当Free Space部分的空间全部被User Records部分替代掉之后,也就意味着这个页使用完了。

5.3.1 记录头信息的秘密

CREATE TABLE page_demo ( 
	c1 INT, 
	c2 INT, 
	c3 VARCHAR ( 10000 ), 
	PRIMARY KEY ( c1 ) 
) CHARSET=ASCII ROW_FORMAT=COMPACT;

在这里插入图片描述

名称大小(bit)描述
预留位11没有使用
预留位21没有使用
delete_mask1标记该记录是否被删除
min_rec_mask1B+树的每层非叶子节点中的最小记录都会添加该标记
n_owned4表示当前记录拥有的记录数
heap_no13表示当前记录在记录堆的位置信息
record_type3表示当前记录的类型
next_record16表示下一条记录的相对位置

在这里插入图片描述

插入数据

INSERT INTO page_demo VALUES
	(1, 100, 'aaaa'), 
	(2, 200, 'bbbb'), 
	(3, 300, 'cccc'),
	(4, 400, 'dddd');

在这里插入图片描述

  • delete_mask

    标记该记录是否被删除。0:没有被删除,1:已经被删除

    被删除的记录不会立即从磁盘上移除,因为移除它们之后把其他的记录在磁盘上重新排列需要性能消耗,所以只是打一个删除标记而已,所有被删除掉的记录都会组成一个垃圾链表,这个链表中的记录占用空间被称为可重用空间,之后如果有新记录插入到表中的话,可能把这些空间覆盖掉。

  • min_rec_mask

    B+树的每层非叶子节点中的最小记录都会添加该标记。0:不是B+树的非叶子节点中的最小记录

  • n_ownd

    表示当前记录拥有的记录数。

  • heap_no

    • 表示当前记录在本的位置。

    • 两条伪记录:最小记录和最大记录,heap_no分别为0和1,也就是说它们的位置最先前。

    • 通过主键来比较大小

    • 由于不是我们定义的记录,所以不存放在User Record,而是放在Infimum + Supremum部分

    • 示意图如下

在这里插入图片描述 在这里插入图片描述

  • record_type

    表示当前记录的类型。0:普通记录,1:B+树非叶节点记录,2:最小记录,3:最大记录

  • next_record

    • 表示从当前记录的真实数据到下一条记录的真实数据的地址偏移量。
    • 单向链表,通过一条记录找到它的下一条记录
    • 按主键大小排序
    • 第一条是Infimum记录,最后一条是Supremum记录
    • 删除记录相当于删除链表中的一个节点

在这里插入图片描述 删除一条数据

DELETE FROM page_demo WHERE c1 = 2;

在这里插入图片描述 可以看出:

  1. 第2条记录还在,只是delete_mask改成了1
  2. 第2条记录的next_record变成了0,意味着没有下一条记录了
  3. 第1条记录的next_record指向了第3条
  4. n_owned从5变成了4

再插入一条数据

INSERT INTO page_demo VALUES(2, 200, 'bbbb');

在这里插入图片描述

InnoDB并没有因为新记录的插入而为它申请新的存储空间,而是直接复用了原来被删除记录的存储空间。

5.4 Page Directory(页目录)

  1. 将所有正常的记录(包括最大和最小记录,但不包括标记为已删除的)划分为几个组
  2. 每个组的最后一条记录的头信息中的n_owned属性表示该组内共有几条记录
  3. 将每个组的最后一条记录的地址偏移量单独提取出来按顺序存储到的尾部,这个地方就是所谓的 Page Directory。页面目录中的这些地址偏移量被称为为(Slot),所以这个页目录就是由组成的。

分组中的记录条数规定:最小记录所在的分组只能有1条记录,最大记录所在的分组能有1 ~ 8条记录,剩下的分组中记录条数的范围是4 ~ 8条之间。

再插入一些数据

INSERT INTO page_demo values
	(5, 500, 'eeee'), (6, 600, 'ffff'), (7, 700, 'gggg'),
	(8, 800, 'hhhh'), (9, 900, 'iiii'), (10, 1000, 'jjjj'), 
	(11, 1100, 'kkkk'), (12, 1200, 'llll'), (13, 1300, 'mmmm'),
	(14, 1400, 'nnnn'), (15, 1500, 'oooo'), (16, 1600, 'pppp');

在这里插入图片描述 所以在一个数据页中查找指定主键值的记录的过程分为两步

  1. 通过二分法确定该记录所在的槽,并找到该槽中主键值最小的那条记录
  2. 通过记录的next_record属性遍历该槽所在的组中的各个记录

5.5 Page Header(页面头部)

反映一个数据页中存储的记录的状态信息。

名称占用空间(byte)描述
PAGE_N_DIR_SLOT2在页目录中的槽数量
PAGE_HEAP_TOP2还未使用的空间最小地址,也就是说从此处开始之后就是Free Space
PAGE_N_HEAP2本页记录的数量(包括最小和最大记录以及标记为删除的记录)
PAGE_FREE2第一个已经标记为删除的记录地址(各个已删除的记录通过next_record也会组合一个单链表,可以被重新利用)
PAGE_GARBAGE2已删除记录占用的字节数
PAGE_LAST_INSERT2最后插入记录的位置
PAGE_DIRECTION2记录插入的方向
PAGE_N_DIRECTION2一个方向连接插入的记录数量
PAGE_N_RECS2该页中记录的数量(不包括最小和最大记录以及被标记为删除的记录)
PAGE_MAX_TRX_ID2修改当前页的最大事务ID,该值仅在二级索引中定义
PAGE_LEVEL2当前页在B+树中所处的层级
PAGE_INDEX_ID8索引ID,表示当前页属于哪个索引
PAGE_BTR_SEG_LEAF10B+树叶子段的头部信息,仅在B+树的Root页定义
PAGE_BTR_SEG_TOP10B+树非叶子段的头部信息,仅在B+树的Root页定义
  • PAGE_DIRECTION

    假如新插入的一条记录的主键值比上一条记录大,我们说这条记录的插入方向是右边,反之则是左边。

  • PAGE_N_DIRECTION

    假设连续几次插入新记录的方向都是一致的,InnoDB会把沿着同一个方向插入记录的条数记录下来。一旦方向改变了,这个值就是清零。

5.6 File Header(文件头部)

File Header对各种类型的页通用,页会以File Header作为第一个组成部分。

名称占用空间(byte)描述
FIL_PAGE_SPACE_OR_CHKSUM4页的校验和(checksum值),通过某种算法计算一个较短的值来代表很长的字符串
FIL_PAGE_OFFSET4页号,唯一的,InnoDB通过页号可以定位一个
FIL_PAGE_PREV4上一个页的页号
FIL_PAGE_NEXT4下一个页的页号
FIL_PAGE_LSN8页面被最后修改时对应的日志序列位置
FIL_PAGE_TYPE2页的类型
FIL_PAGE_FILE_FLUSH_LSN8仅在系统表空间的一个页中定义,代表文件至少被刷新到了对应的 LSN 值
FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID4页属于的表空间
类型名称十六进制描述
FIL_PAGE_TYPE_ALLOCATED0x0000最新分配,还没使用
FIL_PAGE_UNDO_LOG0x0002Undo日志页
FIL_PAGE_INODE0x0003段信息节点
FIL_PAGE_IBUF_FREE_LIST0x0004Insert Buffer空闲列表
FIL_PAGE_IBUF_BITMAP0x0005Insert Buffer位图
FIL_PAGE_TYPE_SYS0x0006系统页
FIL_PAGE_TYPE_TRX_SYS0x0007事务系统数据
FIL_PAGE_TYPE_FSP_HDR0x0008表空间头部信息
FIL_PAGE_TYPE_XDES0x0009扩展描述页
FIL_PAGE_TYPE_BLOB0x000ABLOB页
FIL_PAGE_INDEX0x45BF索引页,也就是我们所说的数据页
  • FIL_PAGE_PREV和FIL_PAGE_NEXT

    上一个和下一个页号,以此建立一个双向链表。 在这里插入图片描述

5.7 File Trailer(文件尾部)

8字节组成

  • 前4字节代表页的校验和,和File Header中的校验和相对应。同步内存数据到磁盘时,会先同步File Header的校验和,完全写完时,File Trailer的校验和也会被写到页的尾部。如果完全同步成功,两个校验和应该是一致的。反之则意味着同步过程出了问题。
  • 后4字节代表页面最后修改时对应的日志序列位置(LSN)

5.8 总结

  1. InnoDB为了不同的目的设计了不同类型的页。
  2. 一个数据页可以被大致分为7个部分
  3. 每个记录的头信息中都有一个next_record属性,形成一个单链表
  4. InnoDB会把页中的记录划分为若干个组,每个组的最后一个记录的地址偏移量作为一个槽,存放在Page Directory中。
  5. 每个数据页的File Header部分都有上一个和下一个页的编号,形成一个双链表
  6. 为保证从内存中同步到磁盘的页的完整性,页的尾部会进行二次校验。