rr级别下能解决不可重复读,因为readview在事务首次快照读时生成并全程复用,其包含m_ids、min_trx_id、max_trx_id和creator_trx_id,通过静态比对行的db_trx_id与readview字段,确保同一事务内多次读取结果一致。

因为 RR 级别下,ReadView 在事务第一次执行快照读时就固定生成,后续所有 SELECT 都复用它,而不是每次查都重新算——这就锁定了“该看到哪些版本”的判断依据。
RR 的 ReadView 是一次性快照,不是动态刷新的
事务启动后,哪怕其他事务提交了新数据,只要当前事务没结束,它的 ReadView 就不会变。它包含三个关键信息:trx_ids(当时活跃事务 ID 列表)、min_trx_id(最小未分配事务 ID)、max_trx_id(最大已提交事务 ID)。后续所有快照读都基于这组静态值做可见性判断。
常见错误现象:
- 以为 SELECT 总是读最新已提交数据 —— 其实 RR 下它只认自己事务启动那一刻的 ReadView
- 在长事务里反复查同一行,发现值没变,但实际已被别人改完并提交 —— 这不是 bug,是 RR 的设计目标
可见性判断只依赖 trx_id 和 ReadView 的静态比对
InnoDB 拿某行记录的 trx_id(即最后修改它的事务 ID)去和当前事务的 ReadView 对比:
- 如果
trx_id ,说明该版本在当前事务启动前就已提交,可见 - 如果
trx_id >= max_trx_id,说明该版本在当前事务启动后才产生,不可见 - 如果
trx_id在trx_ids列表里,说明修改它的事务还活着,不可见 - 否则(
trx_id不在列表中且min_trx_id ),说明它已提交且不在活跃列表中,可见
这个逻辑全程不查当前有哪些事务活跃,只靠初始快照里的 trx_ids 列表做静态过滤。
RC 和 RR 的 ReadView 生成时机差异直接决定一致性强度
RC 每次快照读都新建 ReadView,所以能看到其他事务刚提交的修改;RR 只在第一次快照读时生成,之后全复用——这就保证了同一事务内多次 SELECT 同一条记录,结果必然一致。
使用场景注意点:
- UPDATE、DELETE、SELECT ... FOR UPDATE 属于当前读,不走 ReadView,而是读最新版本并加锁
- 纯 SELECT 是快照读,才受 ReadView 约束
- 如果你在 RR 事务里混用快照读和当前读,可能看到“一部分旧、一部分新”的混合状态,这不是 ReadView 失效,而是读类型不同
真正容易被忽略的是:RR 的一致性视图只对快照读有效,且完全依赖事务启动时刻的 ReadView 快照。一旦事务中执行了当前读,后续快照读仍用老视图,但你已经通过当前读“污染”了业务逻辑中的数据认知——这时候一致性只是机制层面的,不是应用层面的。











