MySQL自增主键唯一性靠innodb_autoinc_lock_mode三种模式协同保障:模式0全表锁、模式1对simple insert用轻量mutex而bulk insert仍表锁、模式2无锁预分配;选错模式会导致插入卡顿、主从错乱或死锁。

MySQL 的自增主键锁不是“必须加的锁”,而是 InnoDB 为保证 AUTO_INCREMENT 列值唯一、不冲突而设计的一套并发控制机制;选错 innodb_autoinc_lock_mode 值,轻则插入变慢、主从复制出错,重则引发死锁或数据不一致。
为什么 INSERT 会卡住?看懂三种锁模式的本质区别
根本问题不在 SQL 写得对不对,而在 innodb_autoinc_lock_mode 如何影响不同 INSERT 类型的行为。InnoDB 把插入分为三类:simple insert(如 INSERT INTO t VALUES ())、bulk insert(如 INSERT INTO t SELECT ...)、mixed-mode insert(如 INSERT INTO t (id, name) VALUES (1,'a'), (NULL,'b'))。同一参数值下,这三类语句的锁行为完全不同:
-
innodb_autoinc_lock_mode = 0:所有类型都用表级AUTO_INC锁,语句执行完才释放 → 并发极低,但 ID 绝对连续、SBR 复制安全 -
innodb_autoinc_lock_mode = 1(MySQL 5.7 默认):simple insert用轻量 mutex 分配 ID,秒级释放;bulk insert仍用表级锁 → 平衡点,多数业务够用 -
innodb_autoinc_lock_mode = 2(MySQL 8.0+ 默认):所有类型都绕过表锁,靠预分配 ID 段实现无锁并发 → 性能最高,但 ID 必然不连续,且binlog_format = STATEMENT下主从可能错乱
binlog_format 是你选 mode 的硬约束
别只盯着性能,先看你的复制格式 —— 这直接决定哪些 mode 能用:
- 如果
binlog_format = STATEMENT:只能用innodb_autoinc_lock_mode = 0或1。mode=2 会导致INSERT ... SELECT在主库分配一批 ID,在从库执行时重新分配,ID 冲突或顺序错乱 - 如果
binlog_format = ROW或MIXED:三种 mode 都安全,优先选2。这是 MySQL 8.0+ 默认设为 2 的前提条件 - 检查命令:
SHOW VARIABLES LIKE 'binlog_format';和SHOW VARIABLES LIKE 'innodb_autoinc_lock_mode';
高并发写入场景下,mode=1 为什么还会堵?
很多人以为设成 mode=1 就万事大吉,结果线上批量导入还是排队 —— 因为 bulk insert(比如 LOAD DATA INFILE 或 INSERT INTO t SELECT ...)在 mode=1 下依然退化为表级锁,且锁持有到整条语句执行完。一个耗时 5 秒的 INSERT SELECT,会阻塞所有其他插入请求。
- 典型症状:
SHOW ENGINE INNODB STATUS中看到waiting for auto-inc lock on xxx - 临时缓解:把大
INSERT SELECT拆成小批次 +simple insert,或改用REPLACE INTO ... VALUES (),(),()形式 - 长期方案:确认 binlog_format 是 ROW 后,升级到 mode=2,并接受 ID 不连续的事实
真正容易被忽略的是:事务回滚本身就会制造“空洞”,和锁模式无关。哪怕 mode=0,只要一个事务申请了 ID=1000 后回滚,这个号就永远消失了。不要指望任何 mode 能让自增 ID 在业务层面“绝对连续”——那是设计误区,不是配置问题。


















