mysql 5.7中insert select卡顿的主因是innodb_autoinc_lock_mode=1下预估行数后一次性加表级auto-inc锁并持锁至语句结束,阻塞其他插入;mysql 8.0通过动态预分配、mdl解耦和安全启用mode=2优化并发性。

MySQL 自增锁本身不慢,真正拖慢批量插入的,是 innodb_autoinc_lock_mode=1 下对 INSERT SELECT 类语句的“预估+全量加锁”行为——它把本可分段获取 ID 的过程,硬生生变成一次表级锁占满整条语句执行时间。
为什么 INSERT SELECT 会卡住其他插入?
不是因为锁太重,而是锁的持有时机和范围不合理:
-
INSERT INTO t1 SELECT * FROM t2执行前,InnoDB 会预估t2行数(哪怕只有 10 行),然后一次性申请并锁定从当前自增值开始的一整段 ID 区间 - 这个 AUTO-INC 锁持续到整个
SELECT+INSERT完成,**不是事务结束,也不是单行插入完成** - 期间所有其他连接的
INSERT(哪怕是单行)都得排队等这把锁,SHOW PROCESSLIST显示Waiting for auto-inc lock - 多个并发的
INSERT SELECT会形成串行队列,CPU 被innodb_autoinc_lock相关函数吃满,但实际写入吞吐极低
mode=2 真的“无锁”吗?为什么 5.7 不敢开?
mode=2 不是真无锁,而是跳过互斥量、直接读写全局计数器——但它在 MySQL 5.7 中不可靠:
- binlog 记录的是语句(STATEMENT)或行变更(ROW),但 mode=2 下 ID 分配顺序与实际执行顺序可能错位
- 如果
sync_binlog != 1或未开启 GTID,主库分配了 ID A~A+99,从库回放时因执行顺序不同,可能重复或跳号 - 错误示例:
REPLACE INTO或带触发器的INSERT SELECT在 mode=2 下极易引发主从不一致,Last_Errno: 1205就是典型表现 - MySQL 8.0 支持 mode=2 的前提是
binlog_format=ROW+enforce_gtid_consistency=ON,缺一不可
怎么验证是不是自增锁在拖慢?
别猜,直接看状态和锁等待链:
- 查
SHOW ENGINE INNODB STATUS\G,重点找LATEST DETECTED DEADLOCK或waiting for auto-inc lock on xxx - 查
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX WHERE trx_state = 'LOCK WAIT',看trx_operation_state是否为setting auto-inc lock - MySQL 8.0 可用
Innodb_autoinc_readiness_wait状态变量统计等待耗时,5.7 没这个指标,得靠performance_schema.data_locks配合分析 - 注意区分:如果
trx_wait_started时间远长于语句实际执行时间,基本就是自增锁瓶颈,不是行锁或 MDL 问题
最容易被忽略的点:自增锁瓶颈往往藏在“看似合理”的批量导入脚本里——比如 Python 多线程跑 LOAD DATA LOCAL INFILE,每个线程都触发独立的 AUTO-INC 锁争抢,而开发者只盯着网络或磁盘 IO 去优化。











