RC默认不加间隙锁,RR默认加临键锁;RC通过半一致性读和早释放锁降低死锁概率,仅对命中行加锁,而RR对扫描范围全加锁并持至事务结束。
间隙锁(gap lock)是 RR 隔离级别为防止幻读而自动启用的机制,它不仅锁住匹配的行,还锁住索引记录之间的“间隙”。比如表中有 id 值 1、5、10,执行 UPDATE t SET name='x' WHERE id BETWEEN 3 AND 8,RR 会锁住 (1,5] 和 (5,10) 这两个间隙,导致其他事务无法插入 id=4 或 id=6;而 RC 只对实际命中的 id=5 加 record lock,间隙完全开放。
死锁常发生在多个事务按不同顺序尝试获取同一组间隙锁时。RC 下没有这种跨间隙的锁竞争,自然大幅降低死锁概率。
当 UPDATE 扫描到已被其他事务加了 X lock 的行时,RC 会触发半一致性读(semi-consistent read):先读该行最新已提交版本,再判断是否满足 WHERE 条件;只对满足条件的行才尝试加锁。RR 则不管内容是否匹配、是否被锁,只要扫描到就直接加 next-key lock 并阻塞。
这意味着:
RC 中 UPDATE 或 DELETE 语句执行完即释放所有未命中行的锁;RR 会把所有扫描过的行(包括不满足 WHERE 的)都加上 next-key lock,并一直持有到事务结束。
例如:UPDATE users SET status=2 WHERE created_at > '2026-01-01' 在 RR 下可能锁住几千行(哪怕只改 3 行),其他事务更新任意一行都会卡住;RC 只锁那 3 行,其余扫描行锁立刻释放——锁持有窗口窄,事务间冲突窗口自然小。
RC 并非完全无间隙锁:在唯一键冲突检查、外键约束校验等场景下,仍会加 gap lock,此时也可能发生 INSERT 死锁。但这属于极少数显式约束场景,而非 RR 那样对每个范围查询都默认启用。
所以真正要警惕的不是“RC 有没有间隙锁”,而是:
间隙锁死锁的减少,本质是把锁的“默认行为”从 RR 的保守覆盖,转向 RC 的按需最小化——但这个“按需”,得靠你写对 SQL、建好索引、理清业务逻辑才能兑现。