好的,我们来系统地讲一下 数据库事务隔离级别之 Repeatable Read(可重复读),包括它的定义、解决的问题、实现原理,以及在不同数据库(特别是 MySQL)的实际表现。


1. 事务隔离级别回顾

SQL 标准定义了 4 个隔离级别(从低到高):

隔离级别可能发生的现象
Read Uncommitted(读未提交)脏读、不可重复读、幻读
Read Committed(读已提交)不可重复读、幻读
Repeatable Read(可重复读)幻读
Serializable(可串行化)无脏读、无不可重复读、无幻读

2. Repeatable Read 定义

Repeatable Read(可重复读)
在同一个事务中,多次读取同一行数据的结果是一样的,即事务执行期间不会出现 不可重复读 的问题。

不可重复读指的是:

  • 第一次查询某条记录
  • 事务还没结束时,其他事务修改了这条记录并提交
  • 你再次查询同一条记录,结果发生变化

在 Repeatable Read 级别下,这种情况不会发生


3. 解决了什么问题?

  • 脏读(Dirty Read):读取到其他事务未提交的数据(已解决)
  • 不可重复读(Non-repeatable Read):同一事务多次读取同一行数据结果不同(已解决)

 Repeatable Read 并没有完全避免:

  • 幻读(Phantom Read):同一事务中,执行相同条件的查询时,返回的行数发生变化(因为别的事务新增或删除了符合条件的记录)

例子:

-- 事务 A
BEGIN;
SELECT * FROM orders WHERE status = 'pending';  -- 返回 3 行

-- 事务 B
BEGIN;
INSERT INTO orders (id, status) VALUES (101, 'pending');
COMMIT;

-- 事务 A 再次执行
SELECT * FROM orders WHERE status = 'pending';  -- 返回 4 行(幻读)
COMMIT;

4. MySQL InnoDB 中的表现

MySQL 的 InnoDB 默认隔离级别就是 Repeatable Read,但它有一个特别之处:

  • 普通 SQL 标准的 Repeatable Read 不能避免幻读
  • MySQL InnoDB 的 Repeatable Read 通过 MVCC + Next-Key Lock 实际上也避免了幻读

MVCC(多版本并发控制)

  • InnoDB 在 RR 隔离级别下使用 一致性视图(Consistent Snapshot)
  • 在事务第一次执行 SELECT 时,会记录一个“版本快照”
  • 之后的查询都会基于这个快照返回数据,即使其他事务提交了新数据,也不会影响当前事务的读取结果

Next-Key Lock

  • 对范围查询加锁时,会锁住索引记录 + 间隙(Gap)
  • 这样可以防止其他事务在范围内插入新行,从而避免幻读

所以:

  • MySQL InnoDB 的 Repeatable Read = SQL 标准的 RR + 防幻读能力
  • 这也是为什么 MySQL 默认 RR 就够大多数业务用了

5. 执行过程示例(MySQL InnoDB)

假设表 users 中有数据:

id | name
1  | Alice
2  | Bob

事务 A:

SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN;
SELECT * FROM users WHERE id = 1;  -- 读到 Alice

事务 B:

BEGIN;
UPDATE users SET name = 'Alice2' WHERE id = 1;
COMMIT;

事务 A:

SELECT * FROM users WHERE id = 1;  -- 依然读到 Alice(不可重复读被避免)
COMMIT;

6. 优缺点

优点

  • 保证同一事务中多次读取相同数据一致
  • 避免脏读和不可重复读
  • 在 MySQL InnoDB 下还能防幻读

缺点

  • 会增加锁的持有时间和开销
  • 并发度比 Read Committed 略低
  • 在其他数据库(如 PostgreSQL)下仍可能有幻读

7. 总结对比

特性Read CommittedRepeatable Read(标准)Repeatable Read(MySQL)
脏读
不可重复读
幻读
实现方式每次查询新快照固定快照固定快照 + Next-Key Lock

好的,那我帮你画两张对比图,分别展示:

  • 标准 SQL 的 Repeatable Read(能防不可重复读,但会出现幻读)MySQL InnoDB 的 Repeatable Read(能防不可重复读,也能防幻读)


  • 1️⃣ 标准 SQL Repeatable Read(会出现幻读)时间轴 →
    事务A: | BEGIN
    | SELECT ... status='pending' → 结果 3 行
    事务B: | BEGIN
    | INSERT status='pending'
    | COMMIT
    事务A: | SELECT ... status='pending' → 结果 4 行 (幻读)
    | COMMIT
  • 关键点:事务 A 的查询结果发生了行数变化,这就是幻读。原因:标准 RR 只保证“同一行数据值一致”,但对范围插入/删除无防护。


  • 2️⃣ MySQL InnoDB Repeatable Read(MVCC + Next-Key Lock)时间轴 →
    事务A: | BEGIN
    | SELECT ... status='pending' (生成一致性视图 Snapshot_1) → 结果 3 行
    事务B: | BEGIN
    | INSERT status='pending' (被 Next-Key Lock 阻塞)
    | 等待...
    事务A: | SELECT ... status='pending' → 仍然 3 行
    | COMMIT
    事务B: | INSERT ... 成功(事务A提交后锁释放)
    | COMMIT
  • 关键点

  • MVCC 确保事务 A 始终看到 Snapshot_1 中的版本,不受后续修改影响。Next-Key Lock 锁住查询范围,阻止事务 B 在范围内插入新记录,防止幻读。


  • 对比表特性标准 RRMySQL InnoDB RR脏读(Dirty Read)❌❌不可重复读❌❌幻读(Phantom Read)✅❌实现方式固定快照固定快照 + 间隙锁