INSERT不加Gap Lock,只加Insert Intention Lock;它与Gap/Next-Key Lock互斥但彼此兼容,用于细粒度位置探测,仅在RR级别默认启用。

INSERT 本身不加 Gap Lock,但会申请 Insert Intention Lock
MySQL 的 INSERT 语句从不直接加 Gap Lock;它加的是 Insert Intention Lock——一种特殊的、轻量级的间隙锁变体。它的作用不是“占住间隙”,而是向 InnoDB 发出信号:“我准备往这个索引间隙里插一条记录”。这个动作会触发 InnoDB 对该间隙上是否已有 Gap Lock 或 Next-Key Lock 的即时检查。
如果此时有其他事务正持有该间隙的 X,GAP 或 X,NEXT-KEY 锁(比如来自未提交的 SELECT ... FOR UPDATE、DELETE 或 UPDATE),你的 INSERT 就会立刻被阻塞或报死锁,现象上看起来像“INSERT 在等间隙锁”,实际是它在等自己能安全插入的许可。
-
Insert Intention Lock之间互不冲突:插id=105和插id=108可以并行 - 但它与任何已存在的
GAP或NEXT-KEY锁互斥 - 这种机制只在
REPEATABLE READ隔离级别下启用;READ COMMITTED下基本不生效(除非唯一约束/外键场景)
什么情况下 INSERT 会“被迫”卷入 Gap Lock 冲突
你看到 INSERT 卡住,并不意味着它自己申请了 Gap Lock,而是它撞上了别人留下的 Gap Lock。最常见源头包括:
- 未提交的范围查询:
SELECT * FROM t WHERE status = 1 FOR UPDATE(status是普通索引)→ 锁住所有匹配值之间的间隙 - 无数据的范围删除:
DELETE FROM t WHERE id BETWEEN 100 AND 200→ 即使表里没有id在该区间的记录,也会锁住(100, 200) - 外键引用表扫描:
SELECT * FROM parent WHERE id > 100 FOR UPDATE,而子表有FOREIGN KEY (parent_id) REFERENCES parent(id)→ InnoDB 为防参照完整性破坏,自动锁(100, +supremum] - 唯一约束冲突检测:
INSERT INTO t (uk_col) VALUES (123)被阻塞时,日志可能误报为 “waiting for insert intention lock”,实则是等待已存在记录上的X,REC_NOT_GAP锁(即 record lock)
如何快速定位谁在 hold 那个 Gap Lock
别只盯着 INSERT 语句本身。Gap Lock 不暴露在 INNODB_TRX 中,也看不到事务 ID,必须靠 performance_schema.data_locks 查:
- 先筛活跃长事务:
SELECT TRX_ID, TRX_STATE, TRX_STARTED 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 = 150,若结果中某行LOCK_DATA显示100, 200且LOCK_MODE含GAP,那就是它
注意:SHOW ENGINE INNODB STATUS 里看到 lock_mode X,GAP + “waiting for insert intention lock”,基本可断定是唯一约束或外键引发,不是普通范围查询。
RC 级别下 INSERT 为什么有时也卡住 Gap Lock
官方明确:READ COMMITTED 理论上禁用 Gap Lock,但有两个例外场景仍会悄悄启用:
-
唯一索引冲突检测:如
INSERT ... ON DUPLICATE KEY UPDATE、REPLACE INTO、INSERT IGNORE。InnoDB 必须提前锁定“可能插入成功”的间隙,否则两个事务同时插同一uk值会绕过约束检查 -
外键约束检查:当被引用表(parent)被范围扫描(如
SELECT ... FOR UPDATE)时,InnoDB 为保参照完整性,强制加 Gap Lock。可通过innodb_locks_unsafe_for_binlog=ON关闭,但会牺牲主从一致性和外键安全
也就是说,即使你设了 SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED,只要表上有唯一索引或外键,INSERT 依然可能因 Gap Lock 被卡——这不是配置失效,是 InnoDB 主动妥协的结果。


















