间隙锁仅在rr级别生效,rc下只加记录锁不锁间隙;它依附于next-key lock存在,由记录锁与前隙组成;等值查询是否加间隙锁取决于索引类型:主键或唯一索引不加,非唯一索引或无索引则加。

间隙锁只在RR级别生效,RC下完全不启用
MySQL的间隙锁(Gap Lock)不是可选功能,而是InnoDB在REPEATABLE READ隔离级别下的硬性行为。如果你执行SET TRANSACTION ISOLATION LEVEL READ COMMITTED,哪怕语句带FOR UPDATE或LOCK IN SHARE MODE,InnoDB也**只加记录锁(Record Lock)**,对间隙不做任何限制。这意味着在RC下,SELECT * FROM t WHERE age > 20 FOR UPDATE只会锁住已存在的age=25、30这两行,但别人仍能插入age=22的新记录——幻读立刻出现。
间隙锁不是独立锁,它依附于Next-Key Lock存在
你不会看到InnoDB单独发出一个“Gap Lock”,它总是和记录锁打包成Next-Key Lock:即“某条记录 + 它前面的间隙”。比如表中现有id为1、5、10的行,执行SELECT * FROM t WHERE id > 5 FOR UPDATE,InnoDB实际加的是两个Next-Key Lock:(5,10](锁记录10 + 间隙(5,10))和(10,+supremum](锁记录10之后所有可能插入位置)。其中(5,10)和(10,+supremum)才是真正的间隙范围。
等值查询是否加间隙锁,取决于索引类型
这是最容易误判的地方:
- 主键或唯一索引上的等值查询(如
WHERE id = 100或WHERE a = 5且a是UNIQUE KEY)→ 只加Record Lock,不加间隙锁 - 非唯一索引或无索引的等值查询(如
WHERE c = 3,c只是普通KEY)→ 加Next-Key Lock,包含间隙 - 任何范围查询(
BETWEEN、>、、<code>LIKE 'abc%'等)→ 默认加Next-Key Lock,只要走索引就覆盖间隙
间隙锁防幻读的本质是阻塞Insert Intention Lock
新行插入时,事务必须先申请Insert Intention Lock——一种特殊的间隙锁。而这个锁会与已有的间隙锁/Next-Key锁冲突。所以当(3,5)被锁住,另一个事务尝试INSERT INTO t (id) VALUES (4)就会被阻塞,直到持有锁的事务提交或回滚。关键点在于:间隙锁锁定的是“空白位置”,不是数据本身;它不关心有没有人查过这个位置,只要语句触发了范围扫描+当前读,间隙就自动被纳入保护范围。
真正容易被忽略的是快照读和当前读的混用:一次SELECT(快照读,无锁)后紧跟UPDATE(当前读,加Next-Key Lock),此时间隙锁已经来不及阻止之前插入的幽灵行。防幻读不是靠“加了锁”就万事大吉,而是靠整个操作序列是否全程落在当前读语义下。











