MySQL读已提交(RC)隔离级别为什么会减少间隙锁死锁?

作者:技术拾光时间:2026-07-18 08:43:08浏览:554

RC隔离级别默认不加间隙锁,只对命中行加记录锁,通过半一致性读减少锁竞争,且执行后立即释放未命中行的锁,降低了死锁概率。RR隔离级别默认加临键锁,扫描范围全加锁并保持到事务结束,死锁风险更高。但RC在唯一键冲突等场景下仍可能隐式加间隙锁。

RC 默认不加间隙锁,RR 默认加临键锁

先直说结论:RC 下默认不加间隙锁,RR 默认加临键锁。RC 通过半一致性读和早释放锁将死锁概率压得很低,只锁命中行;RR 则对扫描范围全加锁,并且要一直攥到事务结束。这两种隔离级别在锁机制上的差异,直接决定了死锁频繁度的天壤之别。

MySQL读已提交(RC)隔离级别为什么会减少间隙锁死锁?

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 下根本没有这种跨间隙的锁竞争,死锁概率自然直线下降。

RC 的半一致性读让 UPDATE 不盲目加锁

当 UPDATE 扫描到一行已经被其他事务加了 X lock 的记录时,RC 会触发半一致性读(semi-consistent read):先读该行最新已提交的版本,判断是否真的满足 WHERE 条件;只有条件成立才尝试加锁。RR 呢?不管三七二十一,扫描到的行先加个 next-key lock 再说,如果锁被占着,就只能阻塞等待。

这意味着什么?

  • 无索引查询在 RC 下最多只锁住最终更新的那几行
  • 同样的语句在 RR 下可能锁住整个扫描范围(全表扫描 + 全表间隙锁),锁等待链一拉长,死锁几乎是早晚的事

RC 锁释放更早,减少锁持有时间重叠

RC 里 UPDATE 或 DELETE 语句执行完,未命中行的锁立刻释放;RR 会把所有扫描过的行(包括不满足 WHERE 条件的)都加上 next-key lock,而且一直攥到事务提交或回滚。

举个例子就清楚了:UPDATE users SET status=2 WHERE created_at > '2026-01-01' 在 RR 下可能锁住几千行(哪怕实际只改了 3 行),其他事务想更新任意一行都得卡住;RC 只锁那 3 行,其余扫描行的锁瞬间释放——锁持有窗口越窄,事务间的冲突窗口自然越小。

RC 下 INSERT 死锁仍可能发生,但诱因完全不同

千万别以为 RC 就完全和间隙锁绝缘了——在唯一键冲突检查、外键约束校验这些场景下,MySQL 照样会加 gap lock,这时 INSERT 死锁依然有可能出现。不过这只是极少数显式约束触发的特殊情形,和 RR 那种对每个范围查询都默认启动间隙锁的“地毯式轰炸”,完全是两码事。

所以真正需要警觉的,不是“RC 到底有没有间隙锁”,而是下面这两点:

  • 别在 RC 下想当然地认为“绝对安全”,忘了唯一索引冲突时隐式间隙锁的存在
  • 别因为应用层没处理好不可重复读,就生硬地靠加锁去硬扛,结果人为造出一堆新死锁点

说到底,间隙锁死锁的减少,本质是把锁的默认行为从 RR 的保守覆盖,转向了 RC 的按需最小化——但这个“按需”,最终还得靠你把 SQL 写对、把索引建好、把业务逻辑理清,才能真正兑现。