read committed 无法避免不可重复读,因其每次 select 都生成新 read view,可看到其他事务已提交的最新修改;而 repeatable read 在首次 select 时固定 read view 并全程复用,从而保证同一事务内多次读取结果一致。

它不解决不可重复读问题——READ COMMITTED 本身就会导致不可重复读。
为什么 READ COMMITTED 无法避免不可重复读
READ COMMITTED 的设计目标是防止脏读,不是保证多次读一致。它的 MVCC 行为是:每次 SELECT 都生成一个新 Read View,只过滤掉未提交的版本,但会看到其他事务「已提交」的最新修改。
- 事务 A 执行第一次
SELECT→ 获取当前已提交数据快照,读到balance = 100 - 事务 B 修改并
COMMIT→balance = 200 - 事务 A 再执行一次
SELECT→ 生成新Read View,读到balance = 200
两次结果不同,这就是不可重复读。这不是 bug,是 READ COMMITTED 的明确定义行为。
代码层面想“绕过”不可重复读,实际在做什么
如果你在应用里用 READ COMMITTED 却期望可重复读效果,通常是在做以下某件事:
- 手动缓存第一次查询结果(比如存在本地变量或 Redis),后续直接复用——这已脱离数据库语义,属于应用层兜底
- 在事务内加锁:比如用
SELECT ... FOR UPDATE读一行,再查一次,虽然仍是 READ COMMITTED,但锁住该行后,别的事务无法改,间接“稳住”值 - 误以为 ORM(如 Spring 的
@Transactional(isolation = Isolation.READ_COMMITTED))能自动同步多次读——它不会,每次SELECT仍独立走快照
真正靠数据库解决不可重复读,只能换隔离级别
MySQL 默认的 REPEATABLE READ 才是为此设计的。它让事务第一次 SELECT 就固定 Read View,后续所有普通读都复用它。
- 确认当前连接真实生效的级别:
SELECT @@transaction_isolation - 临时切换(仅当前会话):
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ - 注意:Navicat、DBeaver 等 GUI 工具常复用连接,设完必须断开重连,否则不生效
- 确保表引擎是
InnoDB:SHOW CREATE TABLE your_table查ENGINE=InnoDB
如果业务强依赖单事务内读一致性,又不想升到 SERIALIZABLE,REPEATABLE READ 是 MySQL 下最直接、开销可控的选择。别在 READ COMMITTED 上硬凑“可重复”,那是对抗机制本身。











