读已提交(RC)和可重复读(RR)是 InnoDB 中基于 MVCC 实现的两个核心隔离级别,二者的核心差异在于 Read View 的生成时机,这直接导致了快照读的一致性、版本可见性判断的不同。
我们从核心差异点、行为对比、实例验证三个维度拆解,结合之前的版本链、Read View 知识,帮你彻底理清。
一、核心差异:Read View 的生成时机
这是 RC 和 RR 隔离级别下 MVCC 行为差异的根源,没有之一。
| 隔离级别 | Read View 生成时机 | 核心影响 |
|---|---|---|
| 读已提交(RC) | 每次执行快照读(普通 SELECT)时,都生成一个新的 Read View | 每次读都会基于最新的活跃事务列表判断版本可见性,能看到其他事务刚提交的版本 |
| 可重复读(RR) | 事务中第一次执行快照读时生成 Read View,后续所有快照读复用这个 Read View | 整个事务内用同一个判断规则,多次读同一数据结果一致,不会看到其他事务提交的版本 |
补充:当前读(
SELECT ... FOR UPDATE/INSERT/UPDATE)在两个隔离级别下行为完全一致------ 都是读取最新版本并加锁,不依赖 Read View 快照。
二、关键行为差异对比
基于 Read View 生成时机的不同,RC 和 RR 在 MVCC 层面会表现出 3 个核心差异,这也是面试高频考点:
| 对比维度 | 读已提交(RC) | 可重复读(RR) |
|---|---|---|
| 快照一致性 | 不保证可重复读:同一事务内多次快照读同一数据,结果可能不同 | 保证可重复读:同一事务内多次快照读,结果完全一致 |
| 版本可见性 | 能看到其他事务在两次快照读之间提交的新版本 | 看不到其他事务在本事务执行期间提交的任何版本 |
| 幻读问题 | 存在幻读:两次 SELECT 可能返回不同数量的行 |
解决幻读:InnoDB 通过 Next-Key Lock (行锁 + 间隙锁)配合 MVCC,避免幻读 |
| Undo Log 清理 | 清理更及时:因为每次读都用新 Read View,旧版本很快不再被引用 | 清理较慢:因为事务复用 Read View,旧版本可能被长时间引用 |
三、实例验证:同一个场景下 RC 和 RR 的不同表现
用一个经典并发场景,直观对比两者差异。前置条件:
user表 id=1 的数据版本链:当前版本(102:王五) → 历史版本1(101:李四) → 历史版本2(0:张三)- 事务 102 未提交时,开启事务 103 执行快照读。
场景流程
| 时间 | 事务 102(未提交) | 事务 103(隔离级别 RC) | 事务 103(隔离级别 RR) |
|---|---|---|---|
| T1 | UPDATE user SET name='王五' WHERE id=1;(未提交) |
开始事务 | 开始事务 |
| T2 | - | SELECT name FROM user WHERE id=1;(快照读) |
SELECT name FROM user WHERE id=1;(快照读) |
| T3 | 提交事务 | - | - |
| T4 | - | SELECT name FROM user WHERE id=1;(快照读) |
SELECT name FROM user WHERE id=1;(快照读) |
结果分析
-
T2 时刻(事务 102 未提交)
- 无论 RC/RR,生成的 Read View 中
m_ids都包含 102(活跃事务)。 - 判断当前版本(102: 王五):
DB_TRX_ID=102在m_ids中 → 不可见。 - 遍历版本链,找到历史版本 1(101: 李四):
DB_TRX_ID=101 < min_trx_id→ 可见。 - RC/RR 结果一致 :返回
李四。
- 无论 RC/RR,生成的 Read View 中
-
T4 时刻(事务 102 已提交)
- RC 隔离级别 :第二次
SELECT会生成新的 Read View ,此时m_ids已不包含 102(事务 102 已提交)。判断当前版本(102: 王五):DB_TRX_ID=102不在m_ids中 → 可见。结果 :返回王五(两次读结果不同,不保证可重复读)。 - RR 隔离级别 :第二次
SELECT复用 T2 生成的 Read View ,m_ids仍包含 102。判断当前版本(102: 王五):DB_TRX_ID=102在m_ids中 → 不可见。继续遍历版本链,返回李四(两次读结果相同,保证可重复读)。
- RC 隔离级别 :第二次
四、总结:RC 和 RR 的 MVCC 差异本质
| 本质 | 读已提交(RC) | 可重复读(RR) |
|---|---|---|
| 核心区别 | 每次快照读刷新 Read View | 事务内复用 Read View |
| 一致性 | 语句级一致性(每次读独立) | 事务级一致性(全程一致) |
| 适用场景 | 对一致性要求低、追求高并发的业务(如电商商品列表) | 对一致性要求高的业务(如金融转账、订单支付) |