全网最详细MVCC讲解,一篇看懂( 三 )


版本链在MVCC中,对于每次更新操作,旧值会被保存到一条undo日志中,即使它是该记录的旧版本 。随着更新次数的增加,所有的版本都会通过roll_pointer属性连接成一个链表,称之为版本链 。
版本链的头节点代表当前记录的最新值 。此外,每个版本还包含生成该版本的事务ID 。
Read View一致性视图,全称 Read View,是用来判断版本链中的哪个版本对当前事务是可见的
Read View 说白了就是事务进行快照读操作时候生成的读视图(Read View),在该事务执行快照读的那一刻,会生成数据库系统当前的一个快照,记录并维护系统当前活跃事务的ID(每个事务开启时 , 都会被分配一个ID,这个ID是递增的) 。
这里有一点要注意一下:Read View只针对 RC 和 RR级别
Read Uncommitted(RU)和 Serializable(串行化)是两个特殊的隔离级别,它们不需要使用 Read View 的主要原因是:

  • Read Uncommitted(RU)隔离级别:在 RU 隔离级别下 , 事务可以读取其他事务尚未提交的数据,即脏读 。这意味着不需要通过 Read View 来限制访问范围,事务可以自由地读取其他事务的未提交数据 。由于没有对可见性进行严格控制 , 因此不需要创建或使用 Read View 。
  • Serializable(串行化)隔离级别:在 Serializable 隔离级别下,事务具有最高的隔离性,确保每次读取都能看到一致的快照 。为了实现这种隔离级别,MySQL使用锁机制来保证事务之间的串行执行 。由于事务按顺序执行,并且不允许并发操作 , 所以不需要使用 Read View 进行可见性判断 。
Read Uncommitted 和 Serializable 隔离级别下的事务规则不涉及基于 Read View 的可见性判断 。RU 允许脏读,而 Serializable 则通过锁机制保证串行执行 。因此,在这两个隔离级别下,不需要创建或使用 Read View 。
Read View 可见性原则Read View 遵循一个可见性原则,将要被修改的数据的 DB_TRX_ID 取出来,与系统当前其他活跃事务的ID去对比 。
如果 DB_TRX_ID 跟 Read View 的属性做了某些比较,不符合可见性,那就通过 DB_ROLL_PTR 回滚指针去取出 Undo Log 中的 DB_TRX_ID 再比较 。
即遍历链表的 DB_TRX_ID (从链首到链尾,即从最近的一次修改查起) , 直到找到满足特定条件的 DB_TRX_ID,那么这个 DB_TRX_ID 所在的记录就是当前事务能看见的最新老版本 。
Read View 会维护以下几个字段:
字段
含义
m_ids
Read View 创建时其他未提交的活跃事务 ID 列表 。创建 Read View时,将当前未提交事务 ID 记录下来,后续即使它们修改了记录行的值,对于当前事务也是不可见的 。m_ids 不包括当前事务自己和已提交的事务(正在内存中) 。
m_creator_trx_id
创建该 Read View 的事务 ID 。
m_low_limit_id
目前出现过的最大的事务 ID+1,即下一个将被分配的事务 ID 。大于等于这个 ID 的数据版本均不可见 。
m_up_limit_id
活跃事务列表 m_ids 中最小的事务 ID,如果 m_ids 为空,则  m_low_limit_idm_up_limit_id  。小于这个 ID 的数据版本均可见 。
Read View 可见性具体判断如下:
  1. 如果被访问版本的 DB_TRX_ID 属性值与 Read View 中的 m_creator_trx_id 值相同,表示当前事务正在访问自己所修改的记录,因此该版本可以被当前事务访问 。
  2. 如果被访问版本的 DB_TRX_ID 属性值小于 Read View 中的 m_up_limit_id 值,说明生成该版本的事务在当前事务生成 Read View 之前已经提交,因此该版本可以被当前事务访问 。
  3. 如果被访问版本的 DB_TRX_ID 属性值大于或等于 Read View 中的  m_low_limit_id 值,说明生成该版本的事务在当前事务生成 Read View 之后才提交,因此该版本不能被当前事务访问 。
  4. 如果被访问版本的 DB_TRX_ID 属性值位于 Read View 的  m_up_limit_id 和  m_low_limit_id 之间(包括边界) , 则需要进一步检查 DB_TRX_ID 是否在m_ids 列表中 。如果在列表中,说明在创建ReadView时生成该版本的事务仍处于活跃状态,因此该版本不能被访问;如果不在列表中,说明在创建 Read View 时生成该版本的事务已经提交,因此该版本可以被访问 。


    推荐阅读