游乐游手机版
首页/数据库/文章详情

MySQL读已提交RC隔离级别减少间隙锁死锁原理

时间:2026-07-18 18:16
RC隔离级别默认不加间隙锁,只对命中行加记录锁,通过半一致性读减少锁竞争,且执行后立即释放未命中行的锁,降低了死锁概率。RR隔离级别默认加临键锁,扫描范围全加锁并保持到事务结束,死锁风险更高。但RC在唯一键冲突等场景下仍可能隐式加间隙锁。
先直说结论: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=4id=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 中,UPDATEDELETE 语句执行完毕后,未命中行的锁会立即释放;而 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 编写、合理的索引设计以及清晰的业务逻辑才能真正落地生效。

来源:https://www.php.cn/faq/2814843.html
上一篇MySQL批量更新表中特定字段值的方法 下一篇phpMyAdmin导出的Laravel SQL文件生产环境报错原因
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
SQL数据库视图简化开发人员编码的实用方法
数据库 · 2026-07-18

SQL数据库视图简化开发人员编码的实用方法

视图适合封装固定逻辑(如固定时间窗口、静态状态过滤)和固定JOIN关系,不宜用于需动态传入参数(如用户ID、日期范围)的查询。视图性能取决于底层索引,而非视图本身。ALTERVIEW仅更新元数据,会短暂阻塞新查询启动,应避开高峰期操作。

MySQL decimal使用常见问题及解决方法
数据库 · 2026-07-18

MySQL decimal使用常见问题及解决方法

MySQL中的DECIMAL类型用于精确数值计算,通过DECIMAL(M,D)定义精度与标度。常见问题包括精度设置不当导致溢出、计算时精度溢出、插入格式错误等。优化建议:合理设置M和D,可用BIGINT存储分单位,关键列建索引,保持版本更新。

MySQL大字段TEXT建立索引导致的性能下降解决方案
数据库 · 2026-07-18

MySQL大字段TEXT建立索引导致的性能下降解决方案

TEXT字段无法直接建立常规B+树索引,前缀索引不支持左模糊查询,全文索引受词长、中文分词和更新延迟限制。高效替代方案是使用MD5摘要索引和垂直拆分,将TEXT字段分离到独立表,主表采用DYNAMIC行格式,避免溢出页IO问题。

金仓数据库迁移避坑指南:从未被质疑的左连接隐患
数据库 · 2026-07-18

金仓数据库迁移避坑指南:从未被质疑的左连接隐患

迁移至金仓KES时,LEFTJOIN若在WHERE中过滤右表非空列,将会触发外连接消除,导致数据结果减少。正确做法是将过滤条件置于ON子句中。金仓KES与主流数据库行为一致,务必通过EXPLAIN仔细检查执行计划,以确保连接语义正确。

PostgreSQL与MySQL数据库日志机制深度对比
数据库 · 2026-07-18

PostgreSQL与MySQL数据库日志机制深度对比

PostgreSQL仅使用一套WAL日志,就同时实现了MySQL的redo、binlog和undo的全部功能,而MySQL因插件式架构需要三套日志并依赖两阶段提交保证一致性。其多版本并发控制通过追加写实现,无需单独的undo日志,但需要定期执行VACUUM操作清理死元组,防止表膨胀。