read view 是 mvcc 实现 rc 和 rr 隔离级别的唯一可见性判断依据,本质是运行时生成的结构体,含 m_ids、min_trx_id、max_trx_id、creator_trx_id 四字段,用于比对版本链中 trx_id 决定数据可见性;rc 每次快照读新建 read view,rr 仅首次快照读创建并复用。

Read View 机制不是“辅助手段”,而是 RC 和 RR 隔离级别下 MVCC 能工作的唯一判断依据——没有它,就无法决定该读哪个版本的数据。
Read View 是什么?不是快照,是可见性规则计算器
Read View 不是把数据拷一份存起来,而是一个运行时生成的结构体,里面记录了当前事务能看到哪些事务修改的版本。关键字段包括:m_ids(创建时活跃的事务 ID 列表)、min_trx_id(活跃事务中最小 ID)、max_trx_id(下一个将分配的事务 ID)、creator_trx_id(本事务自己的 ID)。每次访问聚簇索引记录时,InnoDB 都会用这四个值去比对记录头里的 trx_id 和 roll_pointer,决定是否跳转到上一个版本。
常见错误现象:以为 “RR 下事务启动后数据就冻结了”——其实冻结的是 Read View,不是数据本身;数据仍在被其他事务持续更新、生成新版本。
RC 和 RR 的区别只在 Read View 创建时机
RC 每次 SELECT 都调用 innobase_get_read_view() 新建一个 Read View;RR 只在事务中第一次 SELECT(或 SELECT ... LOCK IN SHARE MODE 等一致性读)时创建,后续复用。
- RC 场景下:事务 B 更新并提交后,事务 A 再执行一次
SELECT,就会拿到新的 Read View,m_ids中不再包含事务 B 的 ID,于是能看见新值 - RR 场景下:事务 A 第一次
SELECT后,即使事务 B 提交了,A 后续所有SELECT都沿用旧 Read View,只要 B 的trx_id在当初的m_ids里或落在[min_trx_id, max_trx_id)区间内,就不可见
参数差异直接影响行为:显式开启事务后不立刻查询(比如先 UPDATE 再 SELECT),RR 下的 Read View 仍会在首次 SELECT 时才建立,不是在 BEGIN 或 START TRANSACTION 时。
为什么 Read Uncommitted 和 Serializable 不用 Read View?
Read Uncommitted 直接读取最新版本的 trx_id 对应记录,跳过所有可见性检查;Serializable 则退化为加锁模型,所有 SELECT 都隐式加上 LOCK IN SHARE MODE,根本绕开 MVCC 路径。
性能影响明显:RC 因频繁构造 Read View,在高并发短事务场景下会有轻微 CPU 开销;RR 复用 Read View 更轻量,但长事务会拖住 purge 线程,导致 undo log 无法及时清理,磁盘空间持续增长。
容易被忽略的关键点:Read View 生效的前提是“一致性读”
只有普通 SELECT(非锁定读)才走 Read View 流程;SELECT ... FOR UPDATE 或 SELECT ... LOCK IN SHARE MODE 会跳过 MVCC,直接加锁读最新版本。这意味着:同一事务中混合使用普通读和锁定读,可能看到“前后不一致”的结果——不是 bug,是设计如此。
另一个盲区:Read View 对二级索引(非聚簇索引)无效。二级索引记录不含 trx_id,其一致性读依赖回表到聚簇索引后,再用 Read View 判断主键记录可见性。如果二级索引覆盖查询(SELECT a FROM t WHERE b=1,且 a,b 都在索引中),则不回表,此时无法应用 Read View,InnoDB 会强制升格为聚簇索引扫描或加锁读。











