ReadView
ReadView(读视图)是 InnoDB 存储引擎在实现 MVCC时获取数据的依据,主要用于一致性读时判断数据版本的可见性。
ReadView 本质上是一个快照,记录了当前系统中活跃事务的信息,帮助判断某个数据版本是否对当前事务可见。
包含了四个核心字段:
- m_ids:当前系统中活跃的事务 ID 列表
- min_trx_id:活跃事务中的最小事务 ID
- max_trx_id:系统尚未分配的下一个事务 ID,即预分配事务id,当前最大事务ID + 1
- creator_trx_id:创建该 ReadView 的事务 ID
版本链数据访问规则

trx_id代表当前事务ID
- trx_id == creator_trx_id,可以访问该版本,说明数据是被当前事务更改的
- trx_id < min_trx_id,可以访问该版本,说明数据已经提交了
- trx_id > max_trx_id,不可以访问该版本。事务实在ReadView生成后才开启的
- min_trx_id <= trx_id <= max_trx_id,如果trx_id不存在于m_ids,说明可以访问该版本,说明数据已经提交。反正则说明数据没提交

由图可见,当事务5进行快照读时,会查询当前活跃的事务。
- 事务2已经提交因此不算活跃事务,事务3、4、5都还没有提交,因此被统计入m_ids
- 最小的活跃事务的id就是事务3
- 预分配事务id就是最大事务id 事务5的id + 1
- ReadView的创造者也是事务5
由上就生成了一个完整的ReadView
undolog版本链
undolog为回滚日志,在增删改操作的时候产生,用来准备数据回滚
- insert时,undolog只会在回滚时需要,事务提交后可被立即删除
- update、delete还会再快照读时需要,不会被立即删除

依旧从上图分析:
- 事务2修改 id为30的记录,将age从30改为3,生成undolog日志,并将回滚指针指向最初的创建事物的undolog
- 事务3将 id为30 的记录 name 修改为A3,生成undolog日志,并将回滚指针指向事务2的undolog
- 事务4将 id为30 的记录 age修改为10,生成undolog日志,并将回滚指针指向事务3的undolog
由此生成了undolog版本链
READ COMMITTED读提交
在事务中每发生一次快照读都会生成一个ReadView

在RC机制下,事务5每次查询都会生成一个ReadView,那么该如何找到可以访问数据的快照呢?上文讲解的undolog版本链给出了答案。
系统会根据版本链数据访问规则,从undolog版本链的头节点依次向后判断,直到找到第一个符合访问规则的undolog,然后将找到的版本的记录的数据返回,这就是我们要读取数据的快照。
由上图可看,两次查询生成了两次不一样的ReadView,那么他们所找到的数据版本也可能会不同,第一次查询时只有事务2的数据可以被访问,第二次查询时事务3的数据也可以被访问了,那么查询返回的数据的版本就是不一致的,数据也是不同的。
因此一个事务中读取同一个数据可能会返回不同的结果,这就是不可重复读。
REPEATABLE READ可重复读
仅在事务开始第一次执行快照读时生成一个ReadView,后续复用这个ReadView

由图可见,两次查询操作对应的ReadView是一致的,他们找到的数据版本也就是一样的 ,查询返回的结果就是相同的。
一个事务中读取同一个数据一定会返回相同的结果,这就是可重复读。
总结
ReadView 的创建时机决定了隔离级别行为:
- RC :语句级快照,每次查询都刷新视图。不可重复读。
- RR :事务级快照,整个事务复用同一视图。可重复读。
但这两个事务隔离级别都没能结果幻读的问题。