rc每次select都新建readview,rr仅首次快照读时创建并复用,核心差异在生成时机与复用逻辑:rc实时感知已提交变更,rr冻结可见性边界,从而分别导致不可重复读与事务内读一致性。

RC每次SELECT都新建ReadView,RR只在首次快照读时创建
关键差异不在结构本身,而在生成时机和复用逻辑。RC下,innobase_get_read_view() 在每次普通 SELECT 执行前被调用,生成一个反映“此刻”活跃事务状态的新 ReadView;RR 下,该函数仅在事务内第一次快照读(如 SELECT、SELECT LOCK IN SHARE MODE)时触发,后续所有快照读都复用同一个实例。
这意味着:RC 能及时感知其他事务的提交,RR 则“冻结”了可见性边界。比如事务 B 提交后,RC 事务 A 下一次 SELECT 就能看到新值;而 RR 事务 A 若已执行过首次 SELECT,后续哪怕等十分钟,只要事务未结束,仍沿用旧 ReadView,看不到 B 的修改。
- 显式
BEGIN或START TRANSACTION不会触发ReadView创建 - 事务中先
UPDATE再SELECT,RR 仍会在那个SELECT时才建ReadView,不是在UPDATE时 - 长事务不释放
ReadView,会阻塞 purge 线程清理 undo log,导致磁盘空间持续增长
ReadView字段含义一致,但m_ids集合内容随时间显著不同
ReadView 结构本身(含 m_ids、min_trx_id、max_trx_id、creator_trx_id)在 RC 和 RR 中完全相同,判断可见性的逻辑也一致:对版本链上每个记录的 trx_id,按规则比对是否落在 [min_trx_id, max_trx_id) 区间、是否在 m_ids 中等。
区别在于:m_ids 是生成时刻的“活跃事务 ID 快照”。RC 的 m_ids 每次都变小(因 B 已提交,不再活跃);RR 的 m_ids 自首次创建后就固定,哪怕其中某些事务早已提交,它也不会更新——这正是“不可重复读被避免”的底层原因。
- RR 的
m_ids可能包含大量已提交但未被 purge 的事务 ID,造成误判为“不可见” - RC 的
m_ids更紧凑,但高频重建带来轻微 CPU 开销,在高并发短查询场景下可测出差异 -
max_trx_id始终是“下一个将分配的事务 ID”,与隔离级别无关,只取决于系统全局计数器
Binlog格式限制RC在STATEMENT模式下不可用
当 binlog_format = 'STATEMENT' 时,MySQL 明确禁止将事务隔离级别设为 READ COMMITTED,否则执行任何写操作都会报错:ERROR 1598 (HY000): Binary logging not possible. Message: Transaction level 'READ-COMMITTED' in InnoDB is not safe for binlog mode 'STATEMENT'。
根本原因是:STATEMENT 格式依赖 SQL 语句重放,而 RC 下同一事务内多次 SELECT 可能返回不同结果,导致主从执行同一条 UPDATE 时依据的读取条件不一致,引发数据偏差。RR 因复用 ReadView,保证了语句级读一致性,所以兼容 STATEMENT。
- 若必须用 RC,binlog_format 应设为
ROW或MIXED - MySQL 5.7+ 默认 binlog_format = 'ROW',此限制影响变小,但线上仍需确认配置
- 该限制与
ReadView实现无直接关系,但它是 RC 在生产环境落地时绕不开的约束
快照读与当前读混用时,ReadView不生效
ReadView 只控制快照读(普通 SELECT),对当前读(SELECT ... FOR UPDATE、UPDATE、DELETE)完全无效。后者总是读取最新已提交版本,并加锁。
典型误区:在 RR 事务中先 UPDATE(当前读,看到最新值并加锁),再 SELECT(快照读,却仍用旧 ReadView,可能读不到刚改的值)。这不是 bug,而是机制设计——ReadView 不负责“让读看到自己刚写的”,它只回答“哪些其他事务的修改对我可见”。
- 想在事务内立即看到自己写的变更,后续
SELECT必须显式加上FOR UPDATE或LOCK IN SHARE MODE - RR 下
SELECT ... FOR UPDATE仍可能触发 gap lock,而 RC 下通常只锁匹配行,不锁间隙 - 不要指望
ReadView解决“事务内读己写”问题,那是存储引擎层缓存或应用层逻辑该管的事
UPDATE + COMMIT),那整个生命周期可能压根没生成过 ReadView —— 它不是事务的标配组件,而是快照读的按需产物。











