Innodb数据页结构
- InnoDB数据页结构
- 一、数据页基础概念
- 二、数据页核心结构
- 1. 头部控制区
- 2. 数据存储区
- 3. 尾部与目录区
- 三、关键机制详解
- 1. 记录链表与删除优化
- 2. 页目录与二分查找
- 3. 空间复用与碎片管理
- 4. 数据页的合并与分裂
- 四、应用与性能影响
- 1. 索引效率
- 2. 插入优化
- 3. 事务与恢复
- 4. 与 Buffer Pool 的关系
- 五、总结
InnoDB数据页结构
一、数据页基础概念
-
页大小:InnoDB默认页大小为16KB,是存储管理的基本单位。
-
页类型:包含索引页(数据页)、Undo页、系统页、Inode页、IBuf页等,本文聚焦 索引页(数据页) ,用于存储表记录。
-
FIL_PAGE_UNDO_LOG
: Undo Log 页,用于存储事务回滚所需的信息。 -
FIL_PAGE_INODE
: Inode 页,用于存储段(Segment)的元数据信息。 -
FIL_PAGE_IBUF
: Insert Buffer 页,用于缓存对二级索引的插入操作。 -
FIL_PAGE_TYPE_ALLOCATED
: 新分配但未使用的页。
-
二、数据页核心结构
数据页由7部分组成,按功能分为三大部分:
1. 头部控制区
- File Header(38字节)
-
功能:跨页管理,包含页号(
FIL_PAGE_OFFSET
)、前后页指针(FIL_PAGE_PREV/NEXT
)、页类型(FIL_PAGE_INDEX
表示数据页)、LSN(FIL_PAGE_FILE_FLUSH_LSN
)和校验和(FIL_PAGE_SPACE_OR_CHKSUM
)。 -
字段详解:
-
FIL_PAGE_OFFSET
: 页号,唯一标识一个页。 -
FIL_PAGE_PREV/NEXT
: 前后页指针,形成双向链表,用于B+树的范围查询。 -
FIL_PAGE_TYPE
: 页类型,标识页的用途。 -
FIL_PAGE_FILE_FLUSH_LSN
: 页面最后一次刷新到磁盘时的LSN。 -
FIL_PAGE_SPACE_OR_CHKSUM
: 页的校验和,使用CRC32算法计算,用于检测页的完整性。 -
应用:通过双向链表连接相邻页,支持B+树索引结构;校验和用于检测页完整性。
- Page Header(56字节)
-
功能:记录页内状态,如记录数(
PAGE_N_RECS
)、空闲位置(PAGE_HEAP_TOP
)、删除记录链表(PAGE_FREE
)、索引层级(PAGE_LEVEL
)、插入方向(PAGE_DIRECTION
)、连续插入次数(PAGE_N_DIRECTION
)和最后插入位置(PAGE_LAST_INSERT
)。 -
字段详解:
-
PAGE_N_RECS
: 页中记录的数量(不包括Infimum和Supremum)。 -
PAGE_HEAP_TOP
: 指向堆中第一个可用空间的指针,也是新纪录插入位置的起点。 -
PAGE_FREE
: 指向已删除记录组成的垃圾链表的头部。 -
PAGE_LEVEL
: 当前页在B+树中的层级,0表示叶子节点。 -
PAGE_DIRECTION
: 最近插入记录的方向(左/右)。 -
PAGE_N_DIRECTION
: 最近连续往相同方向插入记录的次数。 -
PAGE_LAST_INSERT
: 指向最后插入的记录。 -
应用:快速获取页填充状态,指导插入和空间重用,优化页分裂策略。
2. 数据存储区
- User Records & Free Space
-
动态分配:记录按行格式存储,新增记录从Free Space划分空间,用尽后申请新页。
-
行格式细节:InnoDB支持多种行格式 (
ROW_FORMAT
),包括Compact
、Redundant
、Dynamic
和Compressed
。Compact
是最常用的行格式,它具有更高的存储效率。 -
Compact 行格式结构:
-
Record Header (5 字节): 存储记录的元数据信息。
-
变长字段长度列表: 逆序存储变长字段的实际长度,每个长度占用1字节或2字节。
-
NULL值标志位: 如果记录中存在NULL值,则使用该标志位标识哪些字段为NULL。
-
实际数据: 存储记录的实际数据。
-
Record Header 详解:
-
delete_mask (1 bit)
: 标记记录是否被删除。 -
record_type (3 bits)
: 记录类型,0表示普通记录,1表示B+树非叶子节点记录,2表示Infimum记录,3表示Supremum记录。 -
next_record (16 bits)
: 指向下一条记录的偏移量,形成单向链表。 -
heap_no (13 bits)
: 记录在堆中的位置编号。 -
n_owned (4 bits)
: 页目录槽拥有的记录数。 -
min_rec_mask (1 bit)
: B+树的每个非叶子节点都必须包含两条记录,分别是最小记录和最大记录。通过设置min_rec_mask标记,保证非叶子节点中最小记录的min_rec_mask值为1。 -
Dynamic/Compressed 行格式: 当记录中的某些列(如TEXT或BLOB类型)的内容过大时,会使用
Dynamic
或Compressed
行格式,将这些列的数据存储在单独的溢出页中。Compressed
行格式还会对数据进行压缩。 -
删除处理:
delete_mask
标记删除记录加入垃圾链表(PAGE_FREE
指向),空间可重用,减少碎片。 实际上被删除的记录仍然保存在页中,只是通过delete_mask
标记为已删除,并且链入到垃圾链表中。 当有新的记录需要插入时,会优先使用这些被删除记录的空间。PAGE_GARBAGE
统计已删除空间总量,辅助空间分配决策。 -
Free Space 管理: InnoDB 使用链表或者位图来管理 Free Space。 当需要分配空间时,会从 Free Space 链表中找到合适的空闲块进行分配。
- Infimum + Supremum(26字节)
-
虚拟记录:作为逻辑边界,Infimum为最小记录,Supremum为最大记录,初始化时即存在。
-
作用:简化范围查询,维护记录链表的起点和终点。
3. 尾部与目录区
- Page Directory(页目录)
-
槽(Slot)结构:将记录分组,每组最后一条记录(槽)保存地址偏移量,槽按主键排序。 每个槽指向组内最大记录的地址偏移量,通过这个偏移量可以找到组内的记录。
-
分组规则:初始组(Infimum)1条,最大记录组1-8条,其他组4-8条。插入触发动态分裂。
-
n_owned
: 每个槽拥有n_owned
个记录,这个值表示该槽指向的记录是其所在组的最后一条记录,且该槽拥有该组内的所有记录。 当插入一条新记录时,会找到与该记录主键值最接近的槽,然后将该槽的n_owned
值加1。 如果n_owned
值超过8,则会触发槽的分裂。 -
二分查找:通过槽快速定位记录,时间复杂度从O(n)降至O(log n)。
-
查找流程:
-
二分法确定目标槽。
-
遍历槽内记录(组内最多8条),避免全页扫描。
- File Trailer(8字节)
-
校验机制:包含页尾校验和与LSN(日志序列号),与File Header校验和对比,确保页写入完整性。
-
LSN (Log Sequence Number): LSN 是一个单调递增的数值,用于表示事务日志的写入顺序。 File Trailer 中存储的 LSN 表示该页最后一次被修改时的 LSN。 LSN 不仅用于崩溃恢复,还用于支持 MVCC (Multi-Version Concurrency Control)。
-
页尾校验和与页头的校验和用于确保数据页在写入磁盘时没有发生损坏。
三、关键机制详解
1. 记录链表与删除优化
-
单链表结构:记录按主键排序,通过
next_record
串联,支持高效顺序访问。 -
删除优化:
delete_mask
标记删除,空间暂不释放,链入垃圾链表供后续重用,避免频繁整理。 当有新记录插入时,优先使用垃圾链表中的空间。
2. 页目录与二分查找
-
槽管理:
-
每个槽指向组内最大记录,初始槽数为2(Infimum和Supremum)。
-
插入记录时,定位到差值最小的槽,增加
n_owned
值,超过阈值则分裂组。 槽实际上存储的是组内最大的记录的地址偏移量,通过这个偏移量可以找到组内的记录。
-
-
查找流程:
-
二分法确定目标槽。
-
遍历槽内记录(组内最多8条),避免全页扫描。
-
3. 空间复用与碎片管理
-
Free Space重用:删除记录的空间优先分配给新插入,减少空间扩张。 InnoDB 使用链表或者位图来管理 Free Space。
-
PAGE_GARBAGE统计:跟踪已删除空间总量,辅助空间分配决策。
4. 数据页的合并与分裂
-
页分裂: 当一个数据页的空间被用完,需要插入新的记录时,就会发生页分裂。 页分裂会将当前页的数据复制到新的页中,并将当前页分成两个部分,分别指向新的页。
-
页合并: 当一个数据页中的数据被删除到一定程度,导致空间利用率过低时,就会发生页合并。 页合并会将相邻的两个页合并成一个页,从而提高空间利用率。
四、应用与性能影响
1. 索引效率
-
B+树结构:数据页作为叶子节点,通过File Header中的前后页指针形成双向链表,支持范围查询。
-
非叶子节点: B+树的非叶子节点存储的是索引键值和指向子节点的指针。 非叶子节点用于快速定位到叶子节点。
-
页目录加速:槽的二分查找大幅减少索引定位时间,尤其适合大范围扫描。
2. 插入优化
-
顺序插入:通过
PAGE_DIRECTION
和PAGE_N_DIRECTION
统计插入方向,优化页分裂策略。 -
紧凑存储:变长字段逆序存储(如NULL值列表),提升缓存命中率。
-
innodb_fill_factor
** 参数:** 这个参数影响数据页的填充程度,从而影响空间利用率和查询性能。 较高的填充因子可以提高空间利用率,但可能会导致更频繁的页分裂。
3. 事务与恢复
-
LSN机制:File Trailer中的LSN与日志系统协作,确保崩溃恢复时页状态的正确性。 LSN 也用于支持 MVCC。
-
Redo Log: Redo Log 记录了数据页的变更操作,用于在崩溃恢复时重做这些操作,保证数据的一致性。
-
Undo日志:虽未展开,但数据页与Undo页协作支持事务回滚和多版本控制(MVCC)。 Undo Log 记录了数据页的旧版本数据,用于支持事务回滚和 MVCC。
4. 与 Buffer Pool 的关系
-
Buffer Pool 是 InnoDB 存储引擎用于缓存数据页的内存区域。 当需要访问某个数据页时,会先从 Buffer Pool 中查找,如果找到则直接使用,否则需要从磁盘读取到 Buffer Pool 中。
-
Buffer Pool 的大小直接影响数据库的性能。 较大的 Buffer Pool 可以缓存更多的数据页,从而减少磁盘 I/O。
五、总结
InnoDB数据页通过精细的结构设计,实现了高效存储管理、快速数据访问和强一致性保障:
-
存储管理:动态空间分配与碎片重用,平衡空间利用率与性能。
-
查询优化:页目录和有序链表结构,支持高效点查与范围扫描。
-
可靠性:校验和与LSN机制确保数据完整性,应对意外中断。
理解数据页结构对数据库调优(如合理选择行格式、监控页分裂、调整 innodb_fill_factor
参数)、故障排查(如空间碎片分析)和性能优化具有重要指导意义。