插入意向锁本身不阻塞其他插入意向锁,真正阻塞INSERT的是其他事务持有的GAP锁或NEXT-KEY锁;例如插入105被卡住,往往因未提交事务用SELECT ... FOR UPDATE锁住了(100,110)间隙。

插入意向锁到底在等谁?
它本身不阻塞其他插入意向锁,真正卡住你的 INSERT 的,几乎总是另一个事务持有的 GAP 锁或 NEXT-KEY 锁。比如你执行 INSERT INTO t VALUES (105) 卡住,SHOW ENGINE INNODB STATUS 显示在等 INSERT intention lock,但查不到谁在 hold —— 很可能有个事务早先执行了 SELECT * FROM t WHERE i1 BETWEEN 100 AND 110 FOR UPDATE 后没提交,它锁住了间隙 (100, 110),而你的插入位置正好落在其中。
怎么快速定位挡路的 GAP 锁?
别只盯着 INSERT 语句本身,重点查当前活跃事务和它们持有的间隙类锁:
- 先看谁还没提交:
SELECT * FROM information_schema.INNODB_TRX WHERE TRX_STATE = 'RUNNING' - 再查锁详情(MySQL 8.0+):
SELECT ENGINE_TRANSACTION_ID, OBJECT_NAME, INDEX_NAME, LOCK_MODE, LOCK_DATA FROM performance_schema.data_locks WHERE LOCK_TYPE = 'RECORD' AND LOCK_MODE LIKE '%GAP%' OR LOCK_MODE = 'NEXT-KEY' - 关键比对:若你要插
id = 105,而某行LOCK_DATA是100, 110且LOCK_MODE含GAP,那就是它
为什么唯一键冲突后也会看到插入意向锁等待?
这不是插入意向锁在起作用,而是隐式 record lock 升级导致的阻塞:
- 事务 A 执行
INSERT INTO t (uk) VALUES (123),成功获取X, REC_NOT_GAP锁 - 事务 B 同时执行相同语句,在 duplicate key 检查阶段被阻塞,等待 A 释放该 record lock
- 此时
SHOW ENGINE INNODB STATUS可能误报为 “waiting for INSERT intention lock”,实际是 record lock 冲突 - 这种场景下,
performance_schema.data_locks中会看到LOCK_MODE = X, REC_NOT_GAP,而非GAP或INSERT_INTENTION
RR 隔离级别下哪些操作会意外留下 GAP 锁?
最容易被忽略的是“没出错、但也没提交”的查询类加锁操作:
-
SELECT ... FOR UPDATE或SELECT ... LOCK IN SHARE MODE在非唯一索引上执行后,事务未提交 → 持续持有 GAP 锁 - 唯一键冲突后事务未显式
ROLLBACK,InnoDB 可能残留未清理的锁等待链 - 批量导入脚本中用
SELECT ... FOR UPDATE做幂等校验,但并发高时多个事务同时查同一范围 → 大量 GAP 锁堆积 - 注意:
innodb_locks_unsafe_for_binlog=OFF(默认)且隔离级别 ≥REPEATABLE-READ才启用插入意向锁机制
最常被低估的点是:插入意向锁本身几乎不消耗资源,但它的存在会触发更频繁的锁冲突检测;一旦出现等待,说明上游已经有长事务或未收敛的范围锁逻辑——问题不在插入动作,而在锁的源头没管住。











