插入意向锁本身不互斥,仅与Gap/Next-Key锁互斥;其作用是在INSERT前探测间隙安全性,防止幻读,冲突时立即阻塞而非等待。

Insert Intention Lock 本身从不与其他 Insert Intention Lock 互斥——只要插入位置不同,它们就能并存。真正产生互斥的,是它和 Gap Lock / Next-Key Lock 的冲突。
Insert Intention Lock 和 Gap Lock 为什么一碰就阻塞?
Gap Lock 是由 SELECT ... FOR UPDATE、UPDATE 或 DELETE 在非唯一索引或范围条件上触发的,它声明“这个间隙禁止插入”。Insert Intention Lock 则在 INSERT 执行前自动申请,声明“我要往这个间隙插一条记录”。两者作用于同一索引间隙(比如 (10, 20)),InnoDB 认为语义直接对立,不等待、不排队,立刻报死锁或阻塞。
- 常见触发场景:
SELECT * FROM t WHERE age BETWEEN 15 AND 25 FOR UPDATE→ 锁住(15, 25)的 Gap;随后任何往该区间插数据的INSERT都会在加插入意向锁时失败 - Gap Lock 不显式暴露:查
information_schema.INNODB_TRX看不到它,必须用performance_schema.data_locks过滤LOCK_MODE LIKE '%GAP%'才能定位 - 即使没数据,范围操作也会加 Gap Lock:比如
DELETE FROM t WHERE id > 1000,表里当前没有 id > 1000 的记录,照样锁(1000, +∞)
唯一索引下 Insert Intention Lock 冲突的假象
你看到的 “Insert Intention Lock 等待”,大概率是误判。当两个事务同时插入相同唯一键值(如 uk_email = 'a@b.com'),InnoDB 在 duplicate key 检查阶段会尝试对已存在记录加 S 锁,而对方正持有该记录的 X 锁 —— 此时真正在冲突的是 record lock,不是插入意向锁。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
-
SHOW ENGINE INNODB STATUS日志里常写 “waiting for insert intention lock”,但performance_schema.data_locks中实际看到的是LOCK_MODE = X, LOCK_DATA = 'a@b.com',类型为REC_NOT_GAP - 多个唯一索引会让问题更隐蔽:事务 A 持有
uk_email上的 S 锁,事务 B 持有uk_phone上的 S 锁,双方又都试图升级为 X 锁 → 死锁闭环瞬间形成 - 空表插入也容易伪装成这种现象:所有 INSERT 都落在
(−∞, +∞),performance_schema.data_locks查不到 INSERT_INTENTION 记录,说明根本没走到插入意向锁申请阶段,而是唯一约束检查直接卡在 record lock 上
什么情况下 Insert Intention Lock 根本不会出现?
Insert Intention Lock 只在「目标间隙尚无记录、需校验 gap 安全性」时才申请。很多你以为没锁的场景,其实是压根没走到这一步。
- 唯一键冲突提前报错:插入重复
uk_col值时,duplicate key 检查失败,事务直接回滚,performance_schema.data_locks查不到INSERT_INTENTION类型锁 - 隔离级别不是 REPEATABLE READ:READ COMMITTED 下不使用 Gap Lock,自然也不需要 Insert Intention Lock 做 gap 探测
- 表没走索引:比如
WHERE条件列无索引,InnoDB 退化为全表扫描+表级锁,插入意向锁无从谈起 - 插入的是主键/唯一索引已存在的值:直接走 record lock 路径,跳过 gap 安全探测
最麻烦的不是锁本身难理解,而是 Gap Lock 和 Insert Intention Lock 都不显式暴露在常规监控里。你看到的永远是 INSERT 卡住或死锁报错,而根源可能藏在几分钟前一条 SELECT ... FOR UPDATE 里——得靠 performance_schema.data_locks 实时抓取,否则只能靠重放和排除法硬猜。

















