插入意向锁是轻量级间隙探测锁,不阻塞其他插入意向锁,仅在repeatable read下用于幻读预防,生命周期极短;阻塞insert的实为gap/next-key锁。

插入意向锁本身不参与锁竞争
Insert Intention Lock 是一种声明型间隙标记,不是传统意义的排他资源锁。它不阻塞其他 Insert Intention Lock,因为 InnoDB 设计上就允许多个事务在同一个 gap(比如 (10, 30))内并发声明“我要插”,只要最终插入位置不同、不踩到同一行或唯一键冲突点。
常见误判场景:
- 看到
INSERT ... VALUES (20)卡住,以为是另一个 INSERT 在抢插入意向锁 → 实际是对方持有Gap Lock或Next-Key Lock(如SELECT ... FOR UPDATE WHERE id BETWEEN 15 AND 25未提交) - 两个事务同时插相同
id_card = '123'(有唯一索引)→ 此时已跳过插入意向锁阶段,直接进入 record lock 冲突检测,LOCK_MODE显示为X, REC_NOT_GAP,不是INSERT_INTENTION - 在
READ COMMITTED隔离级别下执行 INSERT → 插入意向锁根本不会启用,所谓“不阻塞”其实是压根没加锁
怎么确认你真在用插入意向锁
别依赖 SHOW ENGINE INNODB STATUS 里模糊的 “waiting for insert intention lock” 提示——它常是误报。真实存在插入意向锁,需查 performance_schema.data_locks:
SELECT ENGINE_TRANSACTION_ID, OBJECT_NAME, INDEX_NAME, LOCK_MODE, LOCK_DATA FROM performance_schema.data_locks WHERE LOCK_TYPE = 'RECORD' AND LOCK_MODE = 'INSERT_INTENTION';
如果返回空,说明:
- 表没走索引 → InnoDB 无法精确定位 gap,跳过插入意向锁逻辑
- 唯一键冲突提前触发 duplicate key 检查 → 直接申请记录 X 锁,不再走 gap 安全性预检
- 隔离级别不是
REPEATABLE READ(默认)且innodb_locks_unsafe_for_binlog=OFF未关闭
真正阻塞 INSERT 的从来不是插入意向锁
插入意向锁只是个“探路者”,它被阻塞,说明上游已有更重的锁挡路。典型源头包括:
- 未提交的
SELECT ... FOR UPDATE或LOCK IN SHARE MODE在非唯一索引上锁住了 gap(如WHERE age BETWEEN 80 AND 90) - 批量导入脚本中用加锁 SELECT 做幂等校验,高并发下堆积大量 gap 锁
- 唯一键冲突后事务未显式
ROLLBACK,导致锁等待链残留
此时查 INFORMATION_SCHEMA.INNODB_TRX 和 data_locks 中 LOCK_MODE LIKE '%GAP%' 的记录,比盯着 INSERT_INTENTION 有效得多。
插入意向锁只在“安全探测阶段”存在
它不是为串行化插入而设,而是为避免幻读做的一次轻量级间隙安全性检查。一旦要插的位置确定无记录、且 gap 未被其他事务用 Gap/Next-Key 锁封锁,InnoDB 就立刻释放插入意向锁,转而加真正的 X 行锁写入数据。
最容易被忽略的一点:这个锁生命周期极短,且不可手动控制。你在线上看到它长期存在,往往意味着背后有长事务卡在 gap 锁阶段没释放——问题不在 INSERT 语句本身,而在那个迟迟不提交的 SELECT ... FOR UPDATE 或异常中断的事务。











