innodb_autoinc_lock_mode=2通过预分配id区间实现无锁并发分配,彻底消除简单insert的auto-inc锁等待;但要求binlog_format=row以保证主从一致,且id不连续属正常设计,insert select等语句仍需表级锁。

因为 innodb_autoinc_lock_mode = 2 彻底绕开了 AUTO-INC 表级锁,让简单 INSERT 不再排队等待 ID 分配 —— 这个环节在高并发写入时往往是第一个瓶颈点。
innodb_autoinc_lock_mode=2 怎么消除插入锁等待
MySQL 8.0 默认值 innodb_autoinc_lock_mode=1 对 INSERT INTO t (a) VALUES (1) 这类语句仍用轻量 mutex,但本质仍是串行化 ID 分配;而设为 2 后:
- InnoDB 在内存中维护一个预分配区间(比如当前可用段是 1001–2000),每个线程直接从中“划走”一段,无需加锁
-
INSERT INTO t (a) VALUES (1), (2), (3)这种多值插入也完全无锁,只要缓存没耗尽 - 真正卡住性能的不是磁盘或 CPU,而是多个事务在 AUTO-INC 锁上互相等 —— mode 2 直接把这个等待链砍断
为什么必须 binlog_format=ROW 才能用 mode 2
mode 2 下 ID 分配是并发、非顺序的,如果用 STATEMENT 格式 binlog,主库执行 INSERT INTO t SELECT * FROM src LIMIT 100 时分配了 100 个 ID,但从库重放时可能因执行顺序不同导致 ID 分配结果不一致,引发主从数据错乱。
- MySQL 8.0 启动时会校验:
binlog_format不是ROW就直接报错或降级警告 - 即使你只用简单 INSERT,只要实例里存在任何
INSERT SELECT或REPLACE INTO语句,STATEMENTbinlog 都会让 mode 2 失效 -
SHOW VARIABLES LIKE 'binlog_format'必须返回ROW,且不能被客户端临时 SET 覆盖
mode 2 下 ID 不连续是设计使然,不是 bug
你看到 SHOW TABLE STATUS 里 Auto_increment 从 1001 跳到 1050,再跳到 1120,这不是异常,是 mode 2 正常工作的信号。
- 事务回滚后已分配的 ID 不回收 —— 这是为了避免锁和一致性问题,所有数据库都这么干
- 服务器异常重启后,8.0 会从 redo log 恢复最大已分配值,所以不会重复,但可能跳得更大(比如上次预分配了 1001–2000,只用了前 30 个就宕机,重启后从 2001 开始)
- 如果你的应用层依赖 ID 连续做分页(
WHERE id > ? ORDER BY id LIMIT 10),或者中间件靠 ID 单调性做分库路由,mode 2 会直接打破这种假设
真正容易被忽略的是:mode 2 只对简单 INSERT 有效,INSERT SELECT、REPLACE INTO、LOAD DATA 这些语句依然会触发表级 AUTO-INC 锁 —— 如果你的业务里混着批量导入和 API 写入,光调这个参数不够,得把大语句拆小,或者单独建表隔离。











