MySQL隔离级别:大厂为何偏爱RC?

引言

​ 在刚毕业的时候,初入某家互联网金融公司,作为杭州的某中厂,我看到其数据库的事务隔离级别为 RR,所以很长的一段时间内,我都认为,数据库的隔离级别就应该是 RR。直到后来入职杭州的某电商大厂,我才发现其实在大厂往往会将这个默认的数据库级别更改为 RC,那么为什么会做出这种选择呢?

1. 理解隔离级别:RR 与 RC 的核心差异

​ 当数据库上存在多个事务一起执行的时候,就有可能出现脏读、不可重复读、幻读的问题,为了解决这些问题,就有了隔离级别的概念

​ SQL 的标准事务隔离级别包括:读未提交、读已提交(RC)、可重复读(RR)、串行化;隔离级别的"严格性"依次递升,隔离的越严实,意味着效率就越低,故我们需要根据当前的业务场景去判断什么样的隔离级别适合我们的业务。

​ 我们核心关注 RC 和 RR 这两个级别,他们的核心差异在哪里呢?

读已提交 (RC):一个事务只能读取到其他事务已经提交的数据。它解决了"脏读"问题,但无法避免"不可重复读"和"幻读"。

可重复读 (RR) :一个事务在执行过程中看到的数据,与该事务启动时看到的数据是一致的。换言之,即使其他事务修改并提交了数据,该事务也看不到这些变更。它解决了"脏读"和"不可重复读",但在MySQL的InnoDB引擎中,通过"Next-Key Lock"机制部分解决了幻读

经典八股,务必熟背

脏读:一个事务读取到了另一个尚未提交的事务的修改数据

不可重复读:在同一个事务内,两次读取同一条数据,结果不一致(因为被其他已提交事务修改了)。

幻读:在同一个事务内,两次执行相同的查询,返回的记录集合不一致(因为被其他已提交事务新增或删除了数据)。

2. MySQL 的加锁机制:RR 如何阻止幻读

​ 在大多数的情况下,RC 级别在只会加行锁,所以我们暂不展开说明

​ RR 级别我们上文说了,会通过 Next-Key Lock 进行幻读机制的保证,是药三分毒,是锁就会对性能有所影响

​ 我们先回顾一下 MySQL 的加锁规则(借鉴丁奇的 《MySQL45 讲》)

​ 包含了两个"原则"、两个"优化"和一个"bug"。

​ 原则1:加锁的基本单位是next-key lock。next-key lock是前开后闭区间。

​ 原则2:查找过程中访问到的对象才会加锁。

​ 优化1:索引上的等值查询,给唯一索引加锁的时候,next-key lock退化为行锁。

​ 优化2:索引上的等值查询,向右遍历时且最后一个值不满足等值条件的时候,next-key lock退化为间隙锁。

一个bug:唯一索引上的范围查询会访问到不满足条件的第一个值为止。

​ RR 之所以可以避免幻读,就是借助了 Next_Lock,Next-Key Lock可以理解为是记录锁(锁住某条具体的索引记录)和间隙锁(锁住记录之间的间隔)的组合。

​ 例如,执行 SELECT * FROM users WHERE age BETWEEN 20 AND 30 FOR UPDATE。在RR级别下,这条语句不仅会锁住年龄在20到30岁之间的现有用户记录,还会锁住这个年龄区间所有的"间隙",阻止任何新用户(比如年龄为25岁)的插入。这确实有效地防止了幻读,但代价是显著降低了数据库的写入并发能力。

​ 而在RC级别下,通常只会对现有的记录加锁,不会使用间隙锁,因此其他事务插入新记录的操作基本不受影响,并发性自然更高

3. 大厂为何偏爱 RC

​ 大厂选择RC级别,并非对数据一致性要求低,而是基于高并发场景下的务实权衡。在许多业务逻辑中,幻读的实际影响被高估了。例如电商扣减库存的场景:

复制代码
UPDATE stock SET count = count - 1 WHERE product_id = 'xxx' AND count > 0;

​ 该UPDATE语句本身是原子的,即使在两次查询之间发生幻读(新增了库存记录),也不会影响扣减操作的正确性。在这种情况下,放弃RR的"强一致性"保障,换取更高的并发性能,是更具性价比的选择。

此外,RC级别还有以下优势:

  • 锁竞争更少:避免间隙锁,减少死锁概率,提升系统稳定性
  • 性能更优:降低锁开销,提高事务处理吞吐量
  • 问题更易排查:锁行为更简单,线上问题定位效率更高

​ 因此,大厂在业务允许的范围内,倾向于使用RC级别,并通过应用层逻辑(如乐观锁、状态机、流水表等)弥补RC在一致性上的不足,实现性能与安全的平衡。

相关推荐
一 乐3 小时前
二手车销售|汽车销售|基于SprinBoot+vue的二手车交易系统(源码+数据库+文档)
java·前端·数据库·vue.js·后端·汽车
Databend3 小时前
BendSQL v0.30.3 Web UI 功能介绍
数据库
gAlAxy...4 小时前
Spring 从 0 → 1 保姆级笔记:IOC、DI、多配置、Bean 生命周期一次讲透
数据库·sqlserver
Sherry0074 小时前
【译】🔥如何居中一个 Div?看这篇就够了
前端·css·面试
苦学编程的谢4 小时前
Redis_5_单线程模型
数据库·redis·缓存
Mos_x4 小时前
15.<Spring Boot 日志>
java·后端
mm-q29152227294 小时前
Java并发编程从入门到进阶 多场景实战
java·开发语言
Violet_YSWY4 小时前
任何数据结构的构造或初始化,都应指定大小,避免数据结构无限增长吃光内存【示例】
java·数据结构
SimonKing4 小时前
你的项目还在用MyBatis吗?或许这个框架更适合你:Easy-Query
java·后端·程序员