InnoDB页结构深入:页分裂、页合并、填充因子——B+树底层机制全解析

大家好,我是小耶,写功课只是为了我踩过的坑,你们别再踩了!

InnoDB以页为单位管理数据,默认16KB一页。数据行存储在页里,页与页之间通过B+树组织。

但很多人不知道的是:页不是永远"整整齐齐"的。 新数据插入时,页会分裂;数据删除时,页会合并。这些底层操作直接决定了写入性能、空间利用率和查询效率。

今天把页结构这件事彻底拆开讲清楚。

一、先理清一个概念:页到底是什么

数据页(Data Page) 是InnoDB存储数据的基本单位,默认16KB。一个页里存放多行记录,行与行之间通过单向链表连接。

每个数据页有固定的结构:

组成部分 大小 作用
File Header 38字节 页的通用信息(页号、页类型、上下页指针)
Page Header 56字节 页的状态信息(记录数、空闲空间、最大事务ID)
Infimum + Supremum 26字节 虚拟记录,标记页内记录的下界和上界
User Records 不定 实际存储的行记录
Free Space 不定 未使用的空间
Page Directory 不定 页内记录的稀疏索引,加速页内查找
File Trailer 8字节 校验页的完整性

关键点 :页的File Header中有两个指针------FIL_PAGE_PREVFIL_PAGE_NEXT,指向同一层的相邻页。所有数据页通过这两个指针串成一个双向链表。

二、页分裂:顺序插入 vs 随机插入

页分裂是InnoDB在页空间不足时,把一页拆成两页的过程。

什么情况下触发页分裂?

当一个页的Free Space不足以容纳新插入的记录时,InnoDB会执行页分裂:

  1. 分配一个新的数据页

  2. 把原页中的一部分记录移动到新页

  3. 调整B+树的指针

顺序插入 vs 随机插入------性能差异巨大

顺序插入(自增主键)

如果主键是自增的,新记录总是插入到B+树最右侧的页。这个页通常还有Free Space,插入操作直接写入即可,不会触发频繁的页分裂。

实测数据(100万行顺序插入):页分裂次数约200次 ,插入耗时约12秒

随机插入(UUID/雪花ID)

如果主键是随机的(UUID、雪花ID等),新记录可能插入到B+树的任意位置。如果目标页刚好满了,就需要分裂。

实测数据(100万行随机插入):页分裂次数约8000次 ,插入耗时约45秒

差异原因:顺序插入的页分裂集中在B+树最右侧,分裂频率低;随机插入的页分裂分散在B+树的各个位置,分裂频率高。每次页分裂都需要分配新页、移动记录、调整指针,开销远高于简单的插入操作。

一个容易被忽略的细节 :即使主键是自增的,如果innodb_fill_factor设置不当,也可能导致空间浪费和额外的页分裂。

三、页合并:删除数据后发生了什么

页合并是页分裂的逆过程------当相邻的两个页的记录总大小可以合并到一个页时,InnoDB会执行页合并。

触发条件 :当页中删除的记录超过MERGE_THRESHOLD(默认50%)时,InnoDB会检查相邻页是否可以合并。

页合并的代价

  • 需要移动记录

  • 需要调整B+树指针

  • 可能影响并发性能

生产环境的建议不要依赖页合并来回收空间。 如果业务中有大量删除操作,考虑用OPTIMIZE TABLEALTER TABLE ... ENGINE=InnoDB重建表,一次性回收碎片空间。

四、填充因子:控制页的"预留空间"

innodb_fill_factor是InnoDB的一个参数,控制页中预留多少空间给未来的插入。

默认值:100(即页完全填满才分裂)。

设置建议

  • 顺序插入场景(自增主键):保持100。因为新数据总是插在最右侧,不需要预留空间。

  • 随机插入场景 (UUID/雪花ID):设置为80-90。预留10%-20%的空间给未来的插入,降低页分裂频率。

  • 频繁更新的表:适当降低填充因子(如70-80),为更新操作预留空间。

注意innodb_fill_factor只对新建的表或重建的表生效,不会改变现有表的页结构。

五、页分裂对查询性能的影响

页分裂不仅影响写入性能,也会影响查询性能。

原因:页分裂后,原本连续存储的记录可能被分散到两个页中。查询时需要读取更多的页,I/O次数增加。

一个真实案例:某订单表用UUID做主键,运行半年后查询性能从50ms下降到300ms。检查发现B+树的页分裂严重,索引碎片率超过40%。重建表后,查询性能恢复到60ms。

六、实战:监控页分裂与空间碎片

监控页分裂次数

bash 复制代码
SHOW GLOBAL STATUS LIKE 'Innodb_pages_created';
SHOW GLOBAL STATUS LIKE 'Innodb_pages_written';

Innodb_pages_created持续增长,说明页分裂在频繁发生。

检查表空间碎片

bash 复制代码
SELECT 
    TABLE_NAME,
    DATA_LENGTH,
    INDEX_LENGTH,
    DATA_FREE
FROM information_schema.TABLES 
WHERE TABLE_SCHEMA = 'your_db';

DATA_FREE表示表中已分配但未使用的空间。如果DATA_FREE远大于DATA_LENGTH,说明碎片严重。

重建表回收空间

bash 复制代码
ALTER TABLE your_table ENGINE=InnoDB;

七、小结

InnoDB的页结构是B+树底层机制的核心。页分裂的触发条件、顺序插入与随机插入的性能差异、填充因子的控制作用------这些底层细节直接影响写入性能和空间利用率。选主键时,自增主键之所以优于UUID,根本原因就在于页分裂的频率。理解页结构,才能理解InnoDB为什么这样设计。

小耶在手,SQL 不愁

还有什么想了解的,欢迎留言!小耶一定知无不言言无不尽......我们下次见~

相关推荐
Dawson Zhu1 小时前
大模型记忆系统设计:分层架构与关键技术解析
人工智能·语言模型·架构·aigc·agi
山岚的运维笔记1 小时前
mysql 专业笔记 -- 第 36 章:MySQL 管理
运维·数据库·笔记·后端·mysql·oracle·dba
Dawson Zhu2 小时前
大模型预训练为何普遍单轮遍历?——从 Scaling Laws、数据重复到灾难性遗忘的技术解析
人工智能·语言模型·架构·aigc·agi
阳光宅男@李光熠2 小时前
【电子通识】一起学习TDK的EMC基础——电池兼容设计方法概述
java·前端·数据库
知识的搬运工旺仔2 小时前
唯一索引与 NULL 值:PostgreSQL 主键约束与 NULLS NOT DISTINCT
数据库·后端·sql·postgresql
我的xiaodoujiao2 小时前
Django 基础知识详细图文教程 9-Django 模板引擎 2
开发语言·数据库·后端·django
霸道流氓气质2 小时前
SQL 里的 AND / OR 优先级陷阱:一个括号引发的“数据越界“
mysql
蓝速科技2 小时前
商用复杂环境翻译机耐用性选型指南丨蓝速科技
大数据·运维·数据库·人工智能·科技
烈风逍遥3 小时前
第五篇:通用 LLM 流式对话:前后端联接的完整实现
前端·后端·架构