READ COMMITTED能避免脏读是因为每次SELECT都基于最新已提交数据生成新快照,未提交修改不可见;但它无法防止不可重复读,因每次查询使用不同快照,中间其他事务提交会导致结果变化。

读已提交(READ COMMITTED)能避免脏读,是因为它只允许事务读取其他事务**已提交**的修改。但同一事务内多次查询同一数据时,若中间有别的事务提交了更新,第二次查询就会看到新值——这就构成了不可重复读。
为什么 READ COMMITTED 能防脏读
该级别下,每次普通 SELECT 都会生成一个新的 Read View(一致性快照),只包含当时**已提交**的事务所修改的数据。未提交的变更对它完全不可见,所以不会读到回滚前的“脏数据”。
- 事务 A 修改某行但未 COMMIT → 事务 B 执行 SELECT,查不到这个修改
- 事务 A 后续 ROLLBACK → 事务 B 始终没看到任何异常值
为什么它无法防止不可重复读
因为每次 SELECT 都用“最新”的 Read View,而不是固定一个快照。只要两次查询之间有其他事务提交了更改,第二次 SELECT 就会基于更新后的快照读取,结果自然不同。
- 事务 A 第一次 SELECT:读到 balance = 100
- 事务 B UPDATE + COMMIT → balance 变为 200
- 事务 A 第二次 SELECT:新快照已包含事务 B 的提交,读到 200
关键细节:这不是 bug,是设计使然
READ COMMITTED 的语义就是“读最新已提交”,它追求的是数据新鲜度,而非事务内一致性。MySQL 的 InnoDB 在该级别下通过 MVCC 实现非阻塞读,性能比 REPEATABLE READ 更高,适合对实时性要求高、能容忍短时间不一致的场景(如日志统计、报表预览)。
- 它不锁住读取的行,也不冻结快照,所以并发读写效率好
- 如果你需要两次读结果严格一致,就该换用 REPEATABLE READ
- 注意:MyISAM 引擎不支持 MVCC,设成 READ COMMITTED 也无效
怎么确认当前行为符合预期
执行 SELECT @@transaction_isolation 确认会话级别确实是 READ COMMITTED;再用两个连接复现上述 A/B 事务步骤,观察第二次 SELECT 是否确实变了——变,说明机制生效;不变,反而要检查是否误用了 REPEATABLE READ 或连接未刷新。











