MySQL 8.0 默认未启用 innodb_autoinc_lock_mode=2,仍为 mode=1;mode=1 在含 SELECT/REPLACE/LOAD DATA 的批量插入时升级为语句级 AUTO-INC 表锁,导致其他 INSERT 排队阻塞,而 mode=2 通过预分配 ID 段+原子计数器规避表锁,但必须配合 binlog_format=ROW,否则主从不一致且配置无效。

MySQL 8.0 默认启用 innodb_autoinc_lock_mode=2(交错模式),根本目的不是“去掉锁”,而是用更细粒度、更低开销的分配机制,解决高并发 INSERT 场景下的表级锁瓶颈。
为什么 mode=1 在批量插入时会卡住
MySQL 8.0 之前默认的 innodb_autoinc_lock_mode=1 看似优化,但实际只对简单 INSERT(如 INSERT INTO t VALUES ())跳过表锁;一旦语句含 SELECT、REPLACE 或 LOAD DATA,InnoDB 就强制升级为语句级 AUTO-INC 表锁——锁持有到整条语句执行完。
- 一条
INSERT INTO t SELECT * FROM s LIMIT 10000正在跑,其他所有 INSERT 都得排队等待 -
SHOW PROCESSLIST中频繁出现waiting for auto-inc lock on t - 即使 QPS 很低,单条插入延迟也可能从几毫秒飙升至秒级
- 在
binlog_format=STATEMENT下,主从自增值还可能不一致
mode=2 是怎么绕过表锁的
innodb_autoinc_lock_mode=2 的核心是预分配 ID 段 + 原子计数器,不再依赖语句级锁:
- 每个并发 INSERT 向内存计数器申请一段连续 ID(如 1001–1010),用完再申请
- 分配过程只靠原子操作或轻量互斥量,不阻塞其他 INSERT
- 它不是“无锁”,而是把锁压到最低粒度:不锁表、不锁语句、只同步计数器
- 代价明确:ID 必然不连续(事务回滚、段内未用完、预分配浪费都会产生空洞)
必须同时设 binlog_format=ROW
mode=2 的安全性前提被 MySQL 8.0 强制绑定:
- 执行
SELECT @@binlog_format必须返回ROW,否则启动时警告,主从复制可能丢数据 - STATEMENT 格式下,主库执行
INSERT SELECT分配了 ID 段 A,从库重放时因执行顺序不同可能分配段 B,导致主从 ID 不一致 -
binlog_format=ROW记录的是最终行变更,绕开了自增分配逻辑的差异 - 该限制不可绕过:MySQL 8.0 在配置检查阶段就拒绝
innodb_autoinc_lock_mode=2且binlog_format!=ROW的组合
真正容易被忽略的点
mode=2 生效后,ID 分配变成不可预测的“尽力而为”:
- 应用层若假设“刚插入的 ID 紧邻上一个”,比如做相邻记录关联查询,逻辑会出错
- 分库分表中间件依赖 ID 单调递增做路由(如
id % 4),切换前必须验证是否兼容 -
SHOW TABLE STATUS显示的Auto_increment值跳变是正常现象,不代表异常 - 空洞不是 bug,是设计取舍:性能优先,连续性让渡


















