mysql默认模式innodb_autoinc_lock_mode=1下,simple insert(如明确行数的多行values)用轻量互斥锁,语句结束即释放;而insert select等bulk insert仍持表级auto-inc锁至语句结束。

MySQL在多行INSERT时,自增锁不是“事务结束才释放”,而是语句执行完就立刻释放——但前提是这条INSERT属于Simple insert类型,且innodb_autoinc_lock_mode=1(默认值)。
怎么判断你的INSERT算不算Simple insert
只有满足“插入行数在执行前完全可知”这一条件,才算Simple insert。InnoDB靠这个前提决定能否用轻量互斥锁快速分配自增值。
-
INSERT INTO t (a,b) VALUES (1,2), (3,4), (5,6)✅ 是——三行明确写出 INSERT INTO t (a,b) SELECT x,y FROM s WHERE id ❌ 不是——行数取决于<code>s表查询结果,属Bulk insert-
INSERT INTO t (a,b) VALUES (1,2) ON DUPLICATE KEY UPDATE b=3❌ 不是——即使没真正插入新行,仍会触发自增锁(因为可能要分配ID) -
INSERT INTO t (id,a) VALUES (100,'x'), (NULL,'y')❌ 不是——混合模式(Mixed-mode insert),含NULL需自增,需额外协调
innodb_autoinc_lock_mode不同值对释放时机的影响
这个参数直接决定锁行为,不是“越小越安全”,而是“越小越阻塞”。生产环境几乎都用1,但必须清楚它在什么情况下会退化:
-
innodb_autoinc_lock_mode = 0:所有INSERT类操作都用表级AUTO_INC锁,语句结束释放——但并发插入全排队,吞吐极低 -
innodb_autoinc_lock_mode = 1(默认):Simple insert用轻量互斥锁,生成完自增值立即释放;但只要当前有其他事务正持有AUTO_INC锁(比如一个INSERT ... SELECT还没执行完),Simple insert也会被堵住,被迫等锁 -
innodb_autoinc_lock_mode = 2:所有INSERT都无锁预分配,释放“瞬间完成”,但STATEMENT格式主从复制下会丢数据,严禁在非ROW模式集群中启用
为什么SHOW ENGINE INNODB STATUS里总看到auto-inc lock被占用?
这不是锁没释放,而是某个长事务里的Bulk insert(如INSERT INTO t SELECT ...或LOAD DATA)还在跑,它持有的AUTO_INC锁要到事务提交才释放——这期间所有新INSERT都会卡在等待队列里。
- 查谁在占锁:
SELECT * FROM performance_schema.data_locks WHERE LOCK_TRX_ID IN (SELECT TRX_ID FROM information_schema.INNODB_TRX WHERE TRX_STATE = 'RUNNING');
- 典型诱因:ETL脚本没设事务边界、ORM批量导入没分页、监控误把
LOAD DATA当普通INSERT - 注意:
INSERT IGNORE或REPLACE若含自增列,同样触发锁;回滚后已分配的ID永不复用,跳号≠异常
真正容易被忽略的是:Simple insert的“轻量锁”只管自增值生成,不防其他事务并发修改同一行;而Bulk insert的长持锁又不体现在常规行锁视图里——它只在INNODB STATUS的TRANSACTIONS段末尾以mysql tables in use形式隐晦提示。定位问题时,别只盯data_locks,得结合INNODB_TRX和慢日志交叉验证。











