ReadView(读视图)是快照读 SQL执行时MVCC提取数据的依据,记录并维护系统当前活跃的事务 (未提交的)id。
因为我们知道,快照读在读取的时候并不是读取最新的记录,它读取的有可能是历史版本;刚才我们已经介绍了undo log日志所形成的版本链,在版本链中所产生的这些记录都是历史记录,我们快照读在执行的时候,那么到底读取哪一个历史记录呢?就是有ReadView来决定的,因为在ReadView中它会记录并维护系统当前活跃的事务id,这种获取的事务id指的是未提交的事务id。
ReadView中包含了四个核心字段:
字段 | 含义 |
---|---|
m_ids | 当前活跃的事务ID集合 |
min_trx_id | 最小活跃事务ID |
max_trx_id | 预分配事务ID,当前最大事务ID + 1(因为事务ID是自增的) |
creator_trx_id | ReadView创建者的事务ID |
而在readview中就规定了版本链数据的访问规则:
trx_id 代表当前undolog版本链对应事务ID。
在获取历史版本数据的时候,到底获取的时哪一个历史版本,就是拿当前事务的id,和刚才ReadView中的四个属性进行比对。
条件 | 是否可以访问 | 说明 |
---|---|---|
trx_id == creator_trx_id | 可以访问该版本 | 成立,说明数据是当前这个事务更改的。 我刚刚更新的记录,那肯定是可以读取出来的。 |
trx_id < min_trx_id | 可以访问该版本 | 成立,说明数据已经提交了。 因为当前事务ID比最小的活跃事务ID还小。 |
trx_id > max_trx_id | 不可以访问该版本 | 成立,说明该事务是在 ReadView 生成后才开始的。 |
min_trx_id <= trx_id <= max_trx_id | 如果 trx_id 不在 m_ids 中,可以访问该版本 | 成立,说明数据已经提交。 |
不同的隔离级别,生成ReadView的时机不同:
- READ COMMITTED :在事务中每一次执行快照读时生成ReadView。
- REPEATABLE READ:仅在事务中第一次执行快照读时生成ReadView,后续复用该ReadView。
RC级别原理分析
RC隔离级别下,在事务中每一次执行快照读时生成ReadView。
我们就来分析事务5中,两次快照读读取数据,是如何获取数据的?
在事务5中,查询了两次id为30的记录,由于隔离级别为Read Committed,所以每一次进行快照读 都会生成一个ReadView,那么两次生成的ReadView如下。
那么这两次快照读在获取数据时,就需要根据所生成的ReadView以及ReadView的版本链访问规则, 到undolog版本链中匹配数据,最终决定此次快照读返回的数据。
A. 先来看第一次快照读具体的读取过程:
在进行匹配时,会从undo log的版本链,从上到下进行挨个匹配:
先匹配
这条记录,这条记录对应的 trx_id为4,也就是将4带入右侧的匹配规则中。 ①不满足 ②不满足 ③不满足 ④也不满足 , 都不满足,则继续匹配undo log版本链的下一条。
再匹配第二条
,这条 记录对应的trx_id为3,也就是将3带入右侧的匹配规则中。①不满足 ②不满足 ③不满足 ④也不满足 ,都不满足,则继续匹配undo log版本链的下一条。
再匹配第三条
,这条记 录对应的trx_id为2,也就是将2带入右侧的匹配规则中。①不满足 ②满足 终止匹配,此次快照 读,返回的数据就是版本链中记录的这条数据。
B. 再来看第二次快照读具体的读取过程:
在进行匹配时,会从undo log的版本链,从上到下进行挨个匹配:
先匹配
这条记录,这条记录对应的 trx_id为4,也就是将4带入右侧的匹配规则中。 ①不满足 ②不满足 ③不满足 ④也不满足 , 都不满足,则继续匹配undo log版本链的下一条。
再匹配第二条
,这条记录对应的trx_id为3,也就是将3带入右侧的匹配规则中。①不满足 ②满足 。终止匹配,此次快照读,返回的数据就是版本链中记录的这条数据。
RR级别原理分析
RR隔离级别下,仅在事务中第一次执行快照读时生成ReadView,后续复用该ReadView。 而 RR 是可重复读,在一个事务中,执行两次相同的select语句,查询到的结果是一样的。
那MySQL是如何做到可重复读的呢? 我们简单分析一下就知道了
我们看到,在RR隔离级别下,只是在事务中第一次快照读时生成ReadView,后续都是复用该 ReadView,那么既然ReadView都一样, ReadView的版本链匹配规则也一样, 那么最终快照读返回的结果也是一样的。
所以呢,MVCC的实现原理就是通过 InnoDB表的隐藏字段、UndoLog 版本链、ReadView来实现的。 而 MVCC + 锁
,则实现了事务的隔离性。
而一致性则是由redolog 与 undolog保证。一致性指的是数据在执行之前和执行之后是一致的,也就是说这个事务如果执行失败,需要全部回滚,保证数据执行前后一致;事务如果正常提交,我一定要保证这个数据一定是更新过来了的。