mysql 8.0 升级后批量插入变慢、自增id跳号、主从不一致,大概率是innodb_autoinc_lock_mode默认值从1变为2所致;该模式采用预分配机制提升并发,但对insert ... select等不确定行数操作需扫描估算并预留大量id,引发卡顿、跳号及主从差异,建议在依赖连续id、statement/mixed复制或dump恢复场景下显式设为1。

MySQL 8.0 升级后出现批量插入变慢、自增 ID 跳号、主从不一致,大概率是 innodb_autoinc_lock_mode 默认值从 1 变为 2 导致的 —— 这不是 bug,是设计变更,但必须主动适配。
为什么升级后 INSERT ... SELECT 或 LOAD DATA 突然卡顿?
MySQL 8.0 默认启用 innodb_autoinc_lock_mode=2(“交错模式”),它在批量插入时不加表级 AUTO_INC 锁,而是靠预分配+间隙预留生成自增值。好处是并发高,坏处是:当语句涉及不确定行数的查询(如 INSERT INTO t1 SELECT * FROM t2 WHERE ...),InnoDB 必须先扫描估算数量,再一次性预分配一大段 ID —— 这个估算可能严重偏高,导致锁等待或临时内存压力飙升。
- 典型现象:
SHOW PROCESSLIST中看到状态卡在Writing to net或Copying to tmp table,实际是等 autoinc 预分配完成 - 只影响含
SELECT的批量插入,单行INSERT和简单多值INSERT VALUES (),()不受影响 - 如果业务依赖连续自增 ID(比如用 ID 做分页或外部引用),
mode=2下跳号会更频繁
什么时候该调回 innodb_autoinc_lock_mode=1?
不是所有场景都要改,但以下情况建议显式设为 1(“连续模式”):
- 使用
mysqldump --single-transaction恢复时出现主键冲突(因 dump 文件中 INSERT 顺序和实际执行时 autoinc 分配不一致) - 有 binlog 格式为
MIXED或STATEMENT,且从库执行INSERT ... SELECT时出现自增 ID 不一致 - 应用层假设自增 ID 严格递增(例如用 ID 做时间粗略排序),无法接受跳号
- 已知批量插入数据量稳定且不大(
修改方式(需重启生效):
innodb_autoinc_lock_mode = 1。注意:该参数不可动态设置,
SET GLOBAL 会报错 Variable 'innodb_autoinc_lock_mode' is a read only variable。innodb_autoinc_lock_mode=0 还能用吗?
可以,但不推荐。该模式(“传统模式”)对所有 INSERT 都加表级 AUTO_INC 锁,保证绝对连续和可预测,代价是并发能力归零 —— 任何插入都会阻塞其他插入,哪怕只是单行 INSERT。MySQL 5.1 引入该模式本就是为兼容老版本,8.0 中已明确标记为 legacy,未来可能移除。
- 仅适用于极低并发、强一致性要求、且无法改造业务逻辑的遗留系统
- 若误设为
0,监控会明显看到Innodb_row_lock_waits和Threads_running异常升高 - 不要为了“避免跳号”而选
0;跳号本身不影响数据正确性,真正要解决的是业务是否真依赖连续性
比改参数更可持续的应对方式
硬调参数是兜底手段,长期应推动业务解耦对自增 ID 的隐式依赖:
- 把时间排序逻辑交给
created_at字段,而非id - 对外暴露的 ID 改用 UUID/ULID 或独立发号服务,数据库自增 ID 仅作内键
- 批量导入场景改用
INSERT VALUES (),(),...显式列表(此时即使mode=2也走快速路径,不触发预估) - 确认 binlog_format 设为
ROW(MySQL 8.0 默认),可彻底规避STATEMENT下的主从 autoinc 不一致问题
最易被忽略的一点:很多团队只改了 my.cnf,却没检查 Docker 容器启动参数、K8s ConfigMap 或云数据库控制台覆盖配置 —— 最终生效的永远是最后一个加载的值。











