InnoDB数据页结构

  1. 不同类型的页

页是InnoDB管理存储的基本单位,一个页的大小一般是16KB。类型有:存放表空间头部信息的页、存放ChangeBuffer信息的页、存放INODE信息的页、存放undo日志信息的页。

2. 数据页结构

数据页代表的这块16KB大小的存储空间可以划分为多个部分。

InnoDB数据页结构说明7分部分大致的

名称

中文名

占用空间

简单描述

File Header

文件头部

38字节

页的一些通用信息

Page Header

页面头部

56字节

数据页专用的一些信息

Infimum + Supremum

页面中的最小记录和最大记录

26字节

两个虚拟的记录

User Records

用户记录

不确定

用户存储的记录内容

Free Space

空闲空间

不确定

页中尚为使用的空间

Page Directory

页目录

不确定

页中某些记录的相对位置

File Trailer

文件尾部

8字节

校验也是否完整

3. 记录在页中的存储

在一开始生成页面的时候,其实并没有User Records部分,每当插入一条记录时,都会从Free Space部分申请一个记录大小的空间,并将这空间划分到User Records部分。当Free Space部分的空间全部被User Records部分代替之后,也就以为这个页使用完了。

3.1 记录头信息

记录头信息的属性及描述

CREATE TABLE `page_demo`  (
  `c1` int(255) NOT NULL,
  `c2` int(255) NULL,
  `c3` varchar(10000) NULL,
  PRIMARY KEY (`c1`)
) CHARACTER SET = ascii ROW_FORMAT = COMPACT;

INSERT INTO `test`.`page_demo`(`c1`, `c2`, `c3`) VALUES (1, 100, 'aaaa'),
(2,200,'bbbb'),(3,300,'cccc'),(4,400,'dddd');

这4条记录在InnoDB中的行格式如下(只展示记录头和真实数据),列中数据均用十进制表示:

  • delete_mask

这个属性标记当前记录是否被删除,值为1的时候表示记录被删除掉了,值为0的时候表示记录没有被删除。

可以看出,当删除一条记录时,只是标记删除,实际在页中还没有被移除。这样做的主要目的是,以后如果有新纪录插入表中,可以复用这些已删除记录的存储空间。

  • min_rec_mask

B+树的每层非叶子节点中的最小记录都会添加该标记,并设置为1,否则为0。

  • n_owned

表示当前记录拥有的记录数,页中的数据其实还会分为多个组,每个组会有一个最大的记录,最大记录的 n_owned 就记录了这个组中的记录数。在后面介绍 Page Directory 时会看到这个属性的用途。

  • heap_no

这个属性表示当前记录在本页中的位置。

  • record_type

记录类型:0 表示普通记录,1 表示B+树非叶子节点记录,2 表示最小记录,3 表示最大记录,1xx 表示保留

还是以前面索引结构图来看,上面两层的非叶子节点中的记录 record_type 都应该为 1。最底层的叶子节点应该就是普通记录,record_type 为 0。其实每个页还会有一个最小记录和最大记录,record_type 分别为 2 和 3,这个最小记录和最大记录其实就是后面要说的 Infimum 和 Supremum。

  • next_record

表示从当前记录的真实数据到下一条记录的真实数据的地址偏移量,如果没有下一条记录就是 0,当为负数时,说明当前记录的下一条记录在当前记录的前面。

数据页中的记录看起来就像下图这样,按主键顺序排列后,heap_no 记录了当前记录在本页的位置,然后通过 next_record 连接起来。

Infimum记录是本页中主键最小的用户记录,Supremum记录本页中主键最大的记录。

注意 next_record 指向的是记录头与数据之间的位置偏移量。这个位置向左读取就是记录头信息,向右读取就是真实数据,而且之前说过变长字段长度列表和NULL值列表中都是按列逆序存放的,这时往左读取的标识和往右读取的列就对应上了,提高了读取的效率。

如果删除了其中一条记录,delete_mask 就设置为 1,标记为已删除,next_record 就会设置为 0。其实页中被删除的记录会通过 next_record 形成一个垃圾链表,供以后插入记录时重用空间。

4. Page Directory(页目录)

Page Directory(页目录)大致的原理如下:

将所有正常的记录(包括最大和最小记录,不包括标记为已删除的记录)划分为几个组。怎么划分先不关注。

每个组的最后一条记录(也就是组内最大的那条记录)的头信息中的n_owned属性表示该组内共有几条记录。

将每个组的最后一条记录的地址偏移量单独提取出来按顺序存储到靠近页尾部的地方,这个地方就是所谓的Page Directory。

mysql规定对于最小记录所在的分组只能有 1 条记录,最大记录所在的分组拥有的记录条数只能在 1-8 条之间,剩下的分组中记录的条数范围只能在是 4-8 条之间。比方说现在的page_demo表中正常的记录共有18条,InnoDB会把它们分成5组,第一组中只有一个最小记录,如下所示:

通过Page Directory在一个数据页中查找指定主键值的记录的过程分为两步:

通过二分法确定该记录所在的槽,并找到该槽所在分组中主键值最小的那条记录。

通过记录的next_record属性遍历该槽所在的组中的各个记录。

对于链表的查询性能优化,思想上基本上都是通过二分法实现的。上面介绍的Page Directory,跳跃表和查找树都是如此。

5. Page Header

Page Header 用来记录数据页的状态信息,由14个部分组成,共占用56字节。

  • PAGE_N_DIR_SLOTS

页中的记录会按主键顺序分为多个组,每个组会对应到一个槽(Slot),PAGE_N_DIR_SLOTS 就记录了 Page Directory 中槽的数量。

  • PAGE_HEAP_TOP

PAGE_HEAP_TOP 记录了 Free Space 的地址,这样就可以快速从 Free Space 分配空间到 User Records 了。

  • PAGE_N_HEAP

本页中的记录的数量,包括最小记录(Infimum)和最大记录(Supremum)以及标记为删除(delete_mask=1)的记录。

  • PAGE_FREE

已删除的记录会通过 next_record连成一个单链表,这个单链表中的记录空间可以被重新利用,PAGE_FREE 指向第一个标记为删除的记录地址,就是单链表的头节点。

  • PAGE_GARBAGE

标记为已删除的记录占用的总字节数。

  • PAGE_N_RECS

本页中记录的数量,不包括最小记录和最大记录以及被标记为删除的记录,注意和 PAGE_N_HEAP 的区别。

6. File Header(文件头部)

页头部存的是一个数据页的概要信息,是一个页专有的,而文件头存的是各种页通用的信息,比如页的类型是什么、页的编号是多少、上一页的页号是多少、下一页的页号是多少等等。

状态名称

占用空间

描述

FIL_PAGE_SPACE_OR_CHKSUM

4字节

页的校验和(checksum)值

FIL_PAGE_OFFSET

4字节

页号

FIL_PAGE_PREV

4字节

上一个页的页号

FIL_PAGE_NEXT

4字节

下一个页的页号

FIL_PAGE_LSN

8字节

页面被最后修改时对应的日志序列位置(英文名是:Log SequenceNumber,日志序列号)值

FIL_PAGE_TYPE

2字节

该页的类型

FIL_PAGE_FILE_FLUSH LSN

8字节

仅在系统表空间的一个页中定义,代表文件至少被刷新到了对应的LSN值

FIL PAGE ARCH LOG_NO_OR_SPACE_ID

4字节

页属于哪个表空间

既然有上一页、下一页的定义,说明页与页之间其实是互相连接的,它们之间就像一个双向链表(比如 Java 的 LinkedList)那样,如图所示:

类型名称

十六进制

描述

FIL PAGE TYPE ALLOCATED

0x0000

最新分配,还未使用

FIL PAGE UNDO LOG

0x0002

undo日志页

FIL PAGE INODE

0x0003

存储段的信息

FIL PAGE IBUF FREE LIST

0x0004

Change Buffer 空闲列表

FIL PAGE IBUF BITMAP

0x0005

Change Buffer 的一些属性

FIL PAGE TYPE SYS

0x0006

存储一些系统数据

FIL PAGE TYPE TRX SYS

0x0007

事务系统数据

FIL PAGE TYPE FSP HDR

0x0008

表空间头部信息

FIL PAGE TYPE XDES

0x0009

存储区的一些属性

FIL PAGE TYPE BLOB

0x000A

溢出页

FIL PAGE INDEX

0x45BF

索引页,也就是我们所说的数据页

7. File Trailer(文件尾部)

mysql中内存和磁盘的基本交互单位是页。如果内存中页被修改了,那么某个时刻一定会将内存页同步到磁盘中。如果在同步的过程中,系统出现问题,就可能导致磁盘中的页数据没能完全同步,也就是发生了脏页的情况。为了避免发生这种问题,mysql在每个页的尾部加上了File Trailer来校验页的完整性。File Trailer由8个字节组成:

  • 前4个字节代表页的校验和

这个部分是和File Header中的校验和相对应的。简单理解,就是在数据发生变更前,首先会计算出校验和,在File Header添加检验和,数据处理完成后,会在File Trailer添加校验和。如果File Header和File Trailer的校验和一致则表示数据页是完整的。否则,则表示数据页是脏页。

  • 后4个字节代表页面被最后修改时对应的日志序列位置(LSN)

主要参考资料:

掘金小册《MySQL 是怎样运行的:从根儿上理解 MySQL》

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值