为什么UUID做主键写入慢?InnoDB页分裂的触发机制与实测数据

举报
这个DBA有点耶 发表于 2026/09/21 16:05:01 2026/09/21
【摘要】 InnoDB以16KB的页为单位管理数据,但很多人只知道“页分裂会降低性能”,不清楚页分裂在什么条件下触发、页合并何时发生、填充因子如何影响空间利用率。本文从数据页的物理结构出发,拆解页分裂与页合并的触发机制、顺序插入与随机插入的性能差异、填充因子的作用与调优策略,帮你理解InnoDB存储引擎最底层的运行逻辑。

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

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_PREV和FIL_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 TABLE或ALTER 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。

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

监控页分裂次数:

SHOW GLOBAL STATUS LIKE 'Innodb_pages_created';
SHOW GLOBAL STATUS LIKE 'Innodb_pages_written';

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

检查表空间碎片:

SELECT 
    TABLE_NAME,
    DATA_LENGTH,
    INDEX_LENGTH,
    DATA_FREE
FROM information_schema.TABLES 
WHERE TABLE_SCHEMA = 'your_db';

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

重建表回收空间:

ALTER TABLE your_table ENGINE=InnoDB;

七、小结

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

小耶在手,SQL 不愁

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

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。