自增主键仍会触发页分裂,因最右叶节点页满(16kb)时无法容纳新记录,必须拆分为两页;根本原因是默认innodb_fill_factor=100填满无预留,高并发单行插入下“尾部满裂”频发。

自增主键本身不能完全避免页分裂,但配合 innodb_fill_factor、批量插入和锁模式调整,能显著压低分裂频率与开销。单纯依赖 AUTO_INCREMENT 而不做其他配置,在高并发写入下依然会频繁分裂。
为什么自增主键还会分裂?
自增主键保证插入位置在 B+ 树最右叶节点,但不保证该页有空间。当最右页已满(16KB),新记录仍需触发分裂——原页拆成两个半满页,新记录落进其中一页。这种“尾部满裂”在高并发单行插入场景下非常典型。
- 现象:
Innodb_page_splits持续上涨,INSERT延迟毛刺明显,SHOW ENGINE INNODB STATUS中反复出现Pages split - 根本原因:页填得太满(默认
innodb_fill_factor = 100),没给后续追加留余地 - 注意:事务回滚导致的 ID 跳空(如
INSERT后ROLLBACK)不会引发分裂,只留下逻辑间隙
调低 innodb_fill_factor 控制页预留空间
该参数决定叶节点填充比例,默认 100 表示写满才停;设为 85 表示只填 85%,预留 15% 空间应对连续自增写入,降低分裂概率。
- 动态生效:
SET GLOBAL innodb_fill_factor = 85;,但仅对新建索引或ALTER TABLE ... FORCE重建后的表生效 - 已有表必须重建聚簇索引才能应用:
ALTER TABLE t ENGINE=InnoDB, ALGORITHM=INPLACE;(MySQL 5.6+ 支持 Online DDL) - 别设太低:低于 75 会导致磁盘和内存浪费,且二级索引页不受此参数影响
用批量插入 + 交错锁模式摊薄分裂代价
单条 INSERT INTO t VALUES () 每次都检查页空间,高并发下极易撞上同一满页触发多次分裂;批量插入把检查和潜在分裂摊到一次操作里,而 innodb_autoinc_lock_mode = 2 能避免自增锁争用。
- 推荐批量大小:100–1000 行/次,上限受
max_allowed_packet限制 - 确保事务轻量:避免在同个事务中混杂大更新或长查询,否则 purge 线程被阻塞,影响后续页合并时机
- 检查当前锁模式:
SELECT @@innodb_autoinc_lock_mode;,生产环境应为2(交错模式)
监控是否真的改善了?盯住底层指标,不是 QPS
平均延迟下降或 QPS 上升可能是假象;真正有效的优化会反映在 InnoDB 底层行为上。
- 关键指标对比:
SHOW GLOBAL STATUS LIKE 'Innodb_page_splits';应下降,SHOW GLOBAL STATUS LIKE 'Innodb_page_merge_attempts';应上升(说明空闲页开始被回收) - 查细粒度数据:
SELECT * FROM information_schema.INNODB_METRICS WHERE NAME LIKE '%page%'; - 确认重建生效:
SELECT INDEX_NAME, PAGE_NO, N_RECS FROM information_schema.INNODB_INDEX_STATS WHERE TABLE_NAME='t';观察叶节点记录数是否更均衡
页分裂无法彻底消除,但可通过组合策略把分裂从“每秒多次”压到“每分钟几次”。最容易被忽略的是:innodb_fill_factor 修改后对老表无效,不重建索引等于白调;而重建完若应用仍按旧逻辑往 UUID 主键表里插,空洞几小时内就会复现。











