主键自增插入变慢主因是页分裂;B+树叶节点写满后需分裂,引发I/O、拷贝和锁竞争,可通过调小innodb_fill_factor、批量插入、启用交错锁模式优化。

为什么主键自增插入变慢了?先看页分裂现象
MySQL 的 InnoDB 引擎使用 B+ 树组织主键索引,当新记录的主键值大于当前叶节点最大值时,会追加到最右叶节点。但叶节点有固定大小(默认 16KB),写满后必须分裂——这会触发磁盘 I/O、内存拷贝和锁竞争。常见表现包括:innodb_page_splits 指标突增、INSERT 延迟毛刺、SHOW ENGINE INNODB STATUS 中出现大量 Pages split 日志。
- 叶节点填充率长期低于 70%,说明频繁分裂后未有效合并(
InnoDB不主动合并空闲页) - 使用
uuid或乱序数字作主键时,分裂更剧烈;而AUTO_INCREMENT理论上最友好,但仍有优化空间 - 分裂本身不可免,但可减少其频率与代价
调整 innodb_fill_factor 控制页预留空间
innodb_fill_factor 决定 B+ 树非叶子节点和叶节点的填充比例(默认 100)。设为 80 表示叶节点只填 80%,留出 20% 空间应对后续自增插入,降低分裂概率。
- 生产环境建议从 85 开始尝试,观察
innodb_page_splits是否下降;低于 75 可能浪费磁盘和内存 - 该参数是全局动态变量,修改后仅对新建索引生效;已有表需
ALTER TABLE ... FORCE重建聚簇索引才能应用 - 注意:它不影响唯一二级索引页的填充策略,仅作用于主键(聚簇索引)和普通二级索引的叶节点
批量插入时避免单行 AUTO_INCREMENT 触发多次分裂
即使主键自增,逐条 INSERT INTO t VALUES () 仍可能让每条记录都触发一次页检查与潜在分裂。批量插入能显著摊薄分裂开销:
- 用
INSERT INTO t VALUES (),(),()...一次性插入 100–1000 行(具体上限受max_allowed_packet限制) - 避免在事务中混杂其他大操作,否则长事务会拖住 purge 线程,间接影响页合并时机
- 若业务逻辑必须单条插入,确认
innodb_autoinc_lock_mode=2(交错模式),它能减少自增锁争用,配合innodb_fill_factor效果更稳
监控与验证页分裂/合并是否改善
不能只看 QPS 或平均延迟,要盯住 InnoDB 底层指标:
- 查询
SHOW GLOBAL STATUS LIKE 'Innodb_page_splits'和'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'辅助判断叶节点记录密度 - 注意:页合并(merge)由 purge 线程异步触发,不会实时反映,需观察 5–10 分钟窗口趋势
页分裂无法彻底消除,但把填充率、批量节奏和锁模式三者配准,就能让自增插入稳定在吞吐与延迟的合理区间。最容易被忽略的是:改了 innodb_fill_factor 后不重建表,等于没改。











