可重复读是否能解决幻读?
# 可重复读是否解决了幻读?
答案是:大部分情况下解决了,但在特定情况下(锁操作)并未完全解决。
| 概念 | 说明 |
|---|---|
| 可重复读 (RR) | 解决了不可重复读(即一个事务多次读取同一行数据,结果不同)。 |
| 幻读 (Phantom Read) | 指一个事务多次执行范围查询时,第二次查询的结果集中多出了或少了一些新的/消失的行。 |
在可重复读的情况下,MySQL 引入 MVCC,但 MVCC 并没有真正解决了幻读。
可重复读开启了间隙锁,而间隙锁解决了幻读。
所以在可重复读下用 间隙锁 或 next key lock 才可以防止幻读。不使用间隙锁是无法解决幻读的。其实在读提交下检测唯一索引的唯一性也会开启间隙锁。
注意,这里说的是针对同一条数据。
如果针对同一个表 但不是同一条数据,那么可以说,不遵循前提条件,嗯,就是间隙锁之外,锁不住的就无法解决幻读问题。在间隙锁之内的,就能解决幻读。
看面试官是否认为这是同一条数据吧。
如何解决幻读?
很明显可重复读的隔离级别没有办法彻底的解决幻读的问题,如果我们的项目中需要解决幻读的话也有两个办法:
- 使用串行化读的隔离级别
- MVCC+next-key locks:next-key locks 由 record locks(索引加锁) 和 gap locks(间隙锁,每次锁住的不光是需要使用的数据,还会锁住这些数据附近的数据)
其实幻读也大可不必一定要消除,毕竟幻读也是可以接受的。
但面试中面试官问你如何消除幻读,你回答说 把隔离级别改成 串行化 那是不行的。
# 可重复读的原理
# 1. 核心机制:MVCC (多版本并发控制)
MVCC 允许一个事务读取数据时,看到的是该事务开始时的数据快照,而不是数据最新的修改。这样,即使其他事务正在修改或提交数据,当前事务读取到的数据也不会改变,从而实现了“可重复读”的要求。
MVCC 的主要实现组件:
- 隐藏列(Hidden Columns) 每个 InnoDB 表的行记录中,都会包含几个重要的隐藏字段:
DB_TRX_ID(事务 ID): 记录了对该行数据进行最近一次修改/插入的事务 ID。DB_ROLL_PTR(回滚指针): 指向undo log中的一个版本记录。DB_ROW_ID(行 ID): 如果没有主键,InnoDB 会自动生成一个行 ID。
每个事务开始时,系统会生成一个ReadView(一致性视图),ReadView记录当前活跃事务ID列表,用于判断哪些版本对当前事务可见。
# 2. 临键锁(Next-Key Lock) = 记录锁 + 间隙锁
- 锁定记录本身 + 前面的间隙
- 默认的行锁算法
- 既防止当前记录被修改,又有效防止幻影插入
正是这种 “MVCC(用于普通读) + Next-Key Lock(用于加锁读/写操作)” 的组合,使得 InnoDB 的可重复读隔离级别能够有效地避免幻读现象。
上次更新: 2026-03-13 10:04:40