read committed 隔离级别允许同一事务内多次 select 返回不同结果,因其每次读都生成新 readview 并读取最新已提交版本;需读一致性应改用 repeatable read。

MySQL 的 READ COMMITTED 隔离级别本身不承诺“事务内读一致性”,它只保证不读未提交数据。所谓“数据不一致”,往往不是 bug,而是你误把 RC 当成了 RR 在用。
为什么同一事务里两次 SELECT 结果不同?
这是 RC 的设计行为,不是异常。每次 SELECT 都会生成新的 ReadView,读取当时已提交的最新版本。
- 事务 A 执行第一次
SELECT * FROM order WHERE id = 100→ 返回 status = 'pending' - 事务 B 更新该行并
COMMIT - 事务 A 再执行一次相同
SELECT→ 可能返回 status = 'shipped'
这不是“出错了”,而是 RC 的语义:**每次读都看最新快照**。如果你需要两次读结果一样,就该用 REPEATABLE READ,而不是调高隔离级别之外的其他操作。
SELECT ... FOR UPDATE 后再普通 SELECT 为什么还变?
因为 FOR UPDATE 是当前读,它不改变后续普通 SELECT 的快照策略——在 RC 下,每次普通读仍新建 ReadView。
- 事务 A:
SELECT * FROM t WHERE id = 1 FOR UPDATE→ 加锁并读最新值 - 事务 B: 修改并
COMMIT - 事务 A: 再执行
SELECT * FROM t WHERE id = 1→ 仍可能看到新值(RC 下正常)
别指望加锁能“固化”后续读的视图。锁只管并发写冲突,不管读快照生命周期。真要固定视图,得靠事务启动时的 ReadView,而 RC 不提供这个。
连接池复用导致隔离级别“失效”
应用层用了连接池(如 HikariCP、Druid),但没在获取连接后显式设置隔离级别,就会沿用上次连接的配置。
- 上一个请求设了
SET SESSION transaction_isolation = 'REPEATABLE-READ' - 连接归还池中,又被下一个请求复用 → 新请求实际运行在 RR 下,但代码以为是 RC
- 查
SELECT @@transaction_isolation发现是REPEATABLE-READ,和预期不符
解决方案不是“修 MySQL”,而是让连接池在借出连接时执行初始化 SQL:SET SESSION transaction_isolation = 'READ-COMMITTED'。否则你永远不知道当前连接到底是什么级别。
主从延迟 + RC 查询从库 = 看到“跳变”结果
RC 本身不解决主从不一致问题;如果应用读的是延迟 2 秒的从库,而业务又在主库刚写完就去从库查,就可能出现“先查到旧值、再查到新值”的现象,看起来像 RC 导致不一致,其实只是网络和复制延迟。
- 主库写入后立即返回成功
- 从库还没同步完,第一次读从库 → 旧值
- 几秒后再次读从库 → 新值
这种“不一致”跟隔离级别无关,但容易被误判。关键要分清:是事务并发引起的视图差异,还是基础设施链路带来的时序错乱。
真正难处理的不是 RC 的行为本身,而是开发时默认假设“事务内读必然一致”,却没意识到自己选的是 RC。一旦涉及金额校验、状态流转、幂等判断,这种假设就会在高并发下暴露——它不在文档里写“警告”,但在生产里咬人最狠。











