innodb_autoinc_lock_mode=1下普通insert仍会卡住,因其将表级锁降为轻量mutex保护全局自增计数器,高并发时mutex争用导致等效串行化;而insert select仍强制持表级auto_inc锁至语句结束,加剧阻塞。

因为InnoDB默认用表级AUTO_INC锁分配自增值,所有插入操作必须排队获取这把锁,哪怕只插一行。
innodb_autoinc_lock_mode=1 为什么还会卡住普通 INSERT
默认值 innodb_autoinc_lock_mode = 1 并不等于“无锁”。它只是把锁从“语句级表锁”降为“轻量互斥量(mutex)”,但这个 mutex 仍保护着全局自增计数器。当并发 INSERT 非常高(比如 5000+ QPS),mutex 争用本身就会成为瓶颈——你看到的不是等待表锁,而是 CPU 在 innodb_autoinc_lock 相关函数里空转。
- 现象:
SHOW ENGINE INNODB STATUS中频繁出现waiting for auto-inc lock,但SHOW PROCESSLIST没有明显阻塞语句 - 原因:多个线程同时调用
get_auto_increment(),在 mutex 上自旋或休眠 - 注意:单条
INSERT INTO t VALUES ()不触发表级锁,但高频下 mutex 争用等效于串行化
INSERT SELECT 为什么是锁竞争重灾区
INSERT INTO t1 SELECT ... FROM t2 这类语句在 innodb_autoinc_lock_mode = 1 下仍强制使用表级 AUTO_INC 锁,且锁持续到整个语句执行结束。它本质是“先读再写”,InnoDB 必须预估要插多少行,提前锁住一段 ID 范围,期间其他任何 INSERT 都被拦在外面。
- 典型场景:消息队列消费写入、定时批量同步、日志归档
- 更糟的是:如果
SELECT部分没走索引,还会叠加 Next-Key Lock,形成“读锁 + 自增锁”双重等待 - 验证方式:执行
EXPLAIN FORMAT=JSON看是否含"using_temporary_table": true或扫描行数巨大
为什么 mode=2 不是万能解药
设成 innodb_autoinc_lock_mode = 2 确实能彻底去掉 AUTO_INC 锁,但前提是满足三个硬性条件,缺一不可:
-
binlog_format必须是ROW:否则主从复制时从库可能分配重复 ID,报错1205或数据不一致 - 业务不能依赖 ID 连续性:事务回滚、批量插入部分失败、显式指定大 ID 都会留空洞,
id = last_id + 1类逻辑直接崩 - 避免混用
REPLACE INTO:它每次都会消耗一个新 ID,即使最终因唯一键冲突被回滚,ID 也不回收,空洞加剧且锁竞争未真正缓解
真正容易被忽略的点是:调整 innodb_autoinc_lock_mode 必须改配置文件并重启,SET GLOBAL 只影响新连接,老连接仍用旧值——线上服务里一半连接锁表、一半无锁,问题反而更难定位。











