直接改innodb_autoinc_lock_mode=2仅对insert into t values()类简单插入无锁,批量插入仍持表级锁;需结合select @@binlog_format确认row格式、业务不依赖id连续性、避免mixed-mode写法,并拆分大insert、用insert on duplicate key update替代replace。

直接改 innodb_autoinc_lock_mode = 2 能缓解,但只对 INSERT INTO t VALUES () 类简单插入有效;含 SELECT 的批量写入仍卡在表级锁,必须配合写法改造才真正见效。
怎么确认真是自增锁在拖慢写入?
别猜,先看现象是否匹配真实锁行为:
-
SHOW PROCESSLIST里大量线程状态是Waiting for table level lock - 慢日志中
INSERT语句耗时突增,尤其INSERT INTO t SELECT ...、REPLACE INTO t SELECT ...、LOAD DATA -
SHOW ENGINE INNODB STATUS的TRANSACTIONS部分出现AUTO_INC相关等待 - QPS 上不去,但 CPU 和磁盘 IO 并不高——说明瓶颈不在计算或存储,而在锁分配环节
innodb_autoinc_lock_mode = 2 到底能关掉哪些锁?
它不是“全关”,而是按语句类型区别对待:
-
INSERT INTO t VALUES (),(),...→ 不加 AUTO_INC 表锁,性能提升明显 -
INSERT INTO t SELECT ...或REPLACE INTO t SELECT ...→ 仍退化为语句级表锁,改了参数也没用 -
INSERT INTO t (id, name) VALUES (1001, 'a'), (NULL, 'b')(mixed-mode)→ 同样不受益,预分配机制失效 - ID 必然出现空洞:事务回滚后已预分配的 ID 不回收,比如刚插完 1001,下一条可能是 1057
启用 mode=2 的三个硬性前提缺一不可
跳过任一检查,上线即风险:
- 执行
SELECT @@binlog_format,结果必须是ROW;如果不是,需在配置文件中设binlog_format = ROW并重启 MySQL - 业务不能依赖 ID 连续性:不用 ID 做分页推算、不对外暴露 ID 当单号、没有代码写死
id + 1就是下一条 - 避免混用
INSERT ON DUPLICATE KEY UPDATE+ 显式指定自增字段(如INSERT INTO t (id, name) VALUES (NULL, 'x') ON DUPLICATE KEY UPDATE ...),mode=2 下可能触发内部重试,反而更慢
不改参数也能绕开自增锁瓶颈的实操写法
参数只是辅助,真正压垮并发的是写入模式本身:
- 把大
INSERT SELECT拆成应用层分页 + 批量多值INSERT,例如每次 500–1000 行:INSERT INTO t VALUES (),(),...() - 用
INSERT ON DUPLICATE KEY UPDATE替代REPLACE INTO,前者只在真正插入新行时才推进自增计数器 - 确保主键字段类型足够大(如
BIGINT UNSIGNED),避免空洞加速耗尽 ID 范围 - 监控空洞程度:
SELECT MAX(id) FROM t与实际行数对比,差距过大时需评估是否影响下游系统
最常被忽略的一点:innodb_autoinc_lock_mode = 2 只解决“分配快”,不解决“刷得慢”。如果脏页堆积严重或双写缓冲区争用(尤其 MySQL 8.0.30+),即使锁没了,写入照样卡在落盘环节。得一起查 Innodb_buffer_pool_pages_dirty 和 innodb_doublewrite_files 配置。











