mysql的repeatable read通过首次查询生成readview并全程复用,确保事务内多次读取同一字段结果一致;同时用next-key lock锁定行及间隙,防止并发写冲突和幻读,但无法自动解决应用层“先读后写”导致的逻辑丢失更新。

MySQL 的 REPEATABLE READ 隔离级别通过事务级一致性视图(ReadView)和Next-Key Lock(间隙锁 + 行锁)协同工作,来应对变量(即数据库字段)在并发场景下的修改问题。它不靠“锁定变量本身”,而是锁定数据的访问路径与范围,从而保障事务内读取结果稳定、写入行为可预期。
事务内读取不变:靠 ReadView 快照
在 REPEATABLE READ 下,事务第一次执行 SELECT 时会生成一个 ReadView,后续所有读操作(包括普通查询、UPDATE 中的 WHERE 条件匹配)都基于这个快照判断数据版本是否可见。
- 即使其他事务已提交了对某行的更新,只要该新版本不在当前事务的 ReadView 可见范围内,本事务仍读到旧值
- 这直接避免了“不可重复读”——同一事务中两次读同一字段,结果必然一致
- 注意:该机制只影响 读操作的可见性,不阻止其他事务修改数据(除非加锁)
写操作如何防止覆盖冲突:靠 Next-Key Lock
当事务执行 UPDATE 或 DELETE 时,InnoDB 不仅锁定匹配到的行,还会锁定索引记录之间的“间隙”。这种组合锁叫 Next-Key Lock,是解决幻读和部分并发写冲突的关键。
- 例如:
UPDATE accounts SET balance = balance - 100 WHERE id = 1;会为 id=1 的聚簇索引记录加行锁 - 若条件涉及范围(如
WHERE amount > 100),则会对满足条件的索引区间加 Next-Key Lock,阻止其他事务插入或修改该范围内的新行 - 这意味着:两个事务不能同时对同一行执行写操作(会被阻塞),从而避免脏写和逻辑丢失更新
为什么仍可能遇到“逻辑丢失更新”?
REPEATABLE READ 能防止数据库层面的并发写冲突,但无法自动解决应用层的“先读后写”型竞争,即所谓逻辑丢失更新。
- 典型场景:事务 A 和 B 同时读取 balance=100 → 各自计算后执行
UPDATE ... SET balance = 200→ 最终只有一个生效,另一个被覆盖 - 这不是隔离级别失效,而是业务逻辑未显式加锁或未使用原子操作(如
UPDATE ... SET balance = balance - 100) - 解决方案包括:使用带条件的 UPDATE(
WHERE balance >= 100)、乐观锁(version 字段)、或在应用层加分布式锁
与 READ COMMITTED 的关键区别
两者都用 MVCC,但 ReadView 生成时机不同:
- READ COMMITTED:每次 SELECT 都新建 ReadView → 可能读到其他事务刚提交的新值 → 出现不可重复读
- REPEATABLE READ:整个事务只建一次 ReadView → 所有读基于同一快照 → 读一致性更强
- 写锁策略也不同:RC 下 UPDATE 只锁命中的行;RR 下默认启用 Next-Key Lock,锁范围更广,开销略高但安全性更高










