innodb_fill_factor 默认为 100 是导致高频页分裂的根本原因,因其使叶页填满后才触发分裂;设为 85 并重建表可预留空间抗分裂,配合批量插入与 innodb_autoinc_lock_mode=2 进一步优化。

innodb_fill_factor 默认为 100 是根本原因
自增主键确实保证插入位置总在 B+ 树最右叶节点,但不保证该页有空间。InnoDB 默认把每个叶页填满(16KB)才停,一旦最右页写满,新记录进来就必然触发分裂——原页拆成两个半满页,新记录落进其中一页。这种“尾部满裂”在高并发单行插入下极其高频,表现为 Innodb_page_splits 持续上涨、INSERT 延迟毛刺明显。
-
innodb_fill_factor = 100是静态填满策略,没给后续追加留余地 - 分裂不是“错误”,而是页空间耗尽后的正常扩容行为
- 事务回滚造成的 ID 跳空(如
INSERT后ROLLBACK)不会引发分裂,只留下逻辑间隙
如何让自增主键真正“抗分裂”?
关键不是换主键类型,而是控制页填充节奏:- 把
innodb_fill_factor设为 85(SET GLOBAL innodb_fill_factor = 85),让叶页只填 85%,预留 15% 空间应对连续写入 - 已有表必须重建聚簇索引才能生效:
ALTER TABLE t ENGINE=InnoDB, ALGORITHM=INPLACE - 别设太低(如低于 75):会导致磁盘和内存浪费,且二级索引页不受此参数影响
- 配合
innodb_autoinc_lock_mode = 2(交错锁模式),避免自增锁争用放大分裂感知延迟
批量插入比单条 INSERT 更能压低分裂代价
每条INSERT INTO t VALUES () 都会独立检查页空间,高并发下极易撞上同一满页,反复触发分裂;而批量插入把检查和潜在分裂摊到一次操作里:
- 推荐单次插入 100–1000 行(受
max_allowed_packet限制) - 避免在长事务中混杂大更新,否则 purge 线程被拖住,间接影响页合并时机
- 即使业务必须单条写入,也要确认
innodb_autoinc_lock_mode为 2,并监控Innodb_page_merge_attempts是否同步上升
监控页分裂是否真的被压住了
不能只看 QPS 或平均延迟,得盯底层指标:-
SHOW GLOBAL STATUS LIKE 'Innodb_page_splits'应该下降 -
SHOW GLOBAL STATUS LIKE 'Innodb_page_merge_attempts'应该上升(说明页空闲率改善,合并更活跃) - 查
information_schema.INNODB_INDEX_STATS中的N_RECS/Page_NO比值,判断叶节点实际记录密度
页分裂本身不可免,但高频分裂一定是配置或写法出了问题。最容易被忽略的是:调了 innodb_fill_factor 却没重建表,或者以为设了就全局生效——它只对新建或 FORCE 重建后的索引起作用。











