MySQL InnoDB数据页结构
本文将介绍 MySQL 中 InnoDB 对数据页的存储方式和存储结构。
一、什么是数据页?
InnoDB 将数据存储至磁盘中,并使用内存作为缓存。为了减少磁盘读写次数,InnoDB 将数据划分为若干个页,以页作为磁盘和内存之间交互的基本单位。InnoDB 中页的大小一般为 16KB,InnoDB 会一次最少从磁盘中读取 16KB 的内容至内存中,也会一次最少将内存中 16KB 的内容刷新到磁盘中。
InnoDB 中存在着许多不同类型的页,比如存放表空间头部信息的页、存放 Insert Buffer 信息的页、存放 INODE 信息的页、存放 undo 日志信息的页等等。其中,数据页是其中用于存放记录的页,又名索引页。
二、结构概述
- 所有的数据页会根据页中记录的主键大小排序组成一个双向链表
- 数据页中所有记录(包括最大记录和最小记录)会按主键从小到大的顺序串联成一个单向链表
- 每个数据页会维护一个页目录,用于快速查找记录
一个 InnoDB 数据页的存储空间大致被划分为 7 个部分,其中:
名称 | 中文名 | 占用空间大小 | 简单描述 |
---|---|---|---|
File Header | 文件头部 | 38 字节 | 页的一些通用信息 |
Page Header | 页面头部 | 56 字节 | 数据页专有的一些信息 |
Infimum + Supremum | 最小记录和最大记录 | 26 字节 | 两个虚拟的行记录 |
User Records | 用户记录 | 不确定 | 实际存储的行记录内容 |
Free Space | 空闲空间 | 不确定 | 页中尚未使用的空间 |
Page Directory | 页面目录 | 不确定 | 页中的某些记录的相对位置 |
File Trailer | 文件尾部 | 8 字节 | 校验页是否完整 |
三、File Header - 文件头部
该部分对各种类型的页都通用,描述了页的信息,例如:页的编号、上一页、下一页、页的类型等。
四、Page Header - 页面头部
该部分描述了数据页中记录的状态信息,例如:记录数量、第一条记录地址、槽数、页的校验和等。
五、Infimum + Supremum - 最小记录和最大记录
这是两个虚拟的行记录,它们分别代表最小的记录和最小的记录。
六、User Records - 用户记录 和 Free Space - 空闲空间
记录会按照指定的行格式存储到 Records 之中。
所有记录(包括最大记录和最小记录)会按主键值由小到大的顺序串联成一个单链表。
每次插入一条记录,都会从 Free Space 中申请空间划分到 User Records 中,当 Free Space 中的空间全部被用完,就意味着页使用结束,应该申请新的页。
七、Page Directory - 页面目录
1. 什么是页面目录?
为了加快记录的查找,InnoDB 会在页中维护一个 “页面目录”。
所谓 “页面目录”,就是:
- 将所有记录划分为几个组
- 设置每个分组的最后一条记录的头信息中的 n_owned 属性,它将表示分组的记录数
- 记录每个分组最后一条记录的地址偏移量,称为槽,存储于 Page Directory 中
2. 生成步骤
“页面目录” 的生成步骤:
- 初始时,数据页中只有 “最大记录” 和 “最小记录” 两个 “伪记录”,它们被划分为两个分组
- 每插入一条记录(所有记录组成的单链表会发生相应的更新),都从 “页面目录” 中找到主键值比本记录大的最小槽,将该槽对应的记录的 n_owned 值递增,表示该分组中新增了一条记录
- 当某个分组的记录数等于 8 个以后,若再插入记录,会将分组拆分为两个组
3. 页面目录的使用
如果要寻找某条记录,首先使用二分法找到该记录位于的分组的槽,再获取该槽临近的更小槽,从而获取该记录所在分组的最大记录和最小记录,遍历即可获取该记录。
八、File Trailer - 文件尾部
File Trailer 部分用于检测页面的完整性,由两个部分构成:
页的校验和:
和 File Header 中的校验和相对应
File Header 中的校验和在内存中计算,会首先被同步到磁盘中;
File Trailer 中的校验和在完全写入到磁盘后计算
File Header 和 File Trailer 中的检验和会会进行比对,如果不相同,则意味着同步中间存在错误
页面被最后修改时对应的日志序列位置:用于校验页面的完整性
参考
- MySQL 技术内幕
- MySQL 实战 45 讲
- MySQL 是怎样运行的:从根儿上理解 MySQL