MVCC之ReadView

MVCC

MVCC (Multi-Version Concurrency Control) ,即多版本并发控制,利用记录的版本链和ReadView,来控制并发事务访问相同记录时的行为。ReadView即一致性视图,用来判断版本链中的哪个版本是当前事务可见的。

版本链

在每次更新该记录后,都会将旧值放到一条undo日志中。随着更新次数的增多,所有的版本都会被roll_pointer属性连接成一条链表,这个链表就称之为版本链。

ReadView包含的内容

  • m_ids 。在生成ReadView时,当前系统中活跃的读写事务的事务id列表,即还未提交
  • min_trx_id 。在生成ReadView时,当前系统中活跃的读写事务中最小的事务id;也就是m_ids中 的最小值。
  • max_trx_id 。在生成ReadView时,系统应该分配给下一个事务的事务id值。
  • creator_trx_id 。生成该ReadView的事务的事务id。

如何通过ReadView来判断记录的某个版本是可见的?(小于、等于、不在、坚持回溯)

  • 如果trx_id == creator_trx_id,则表明当前事务在访问它自己修改过的记录,所以该版本可以被当前事务访问。
  • 如果trx_id < min_trx_id,则表明生成该版本的事务在当前事务生成ReadView之前已经提交了,所以该版本可以被当前事务访问。
  • 如果trx_id >= max_trx_id,则表明生成该版本的事务在当前事务生成ReadView之后才开启,所以该版本不可以被当前事务访问。
  • 如果trx_id in m_ids,说明创建ReadView时生成该版本的事务还是活跃的,该版本不可以被访问。
  • 如果trx_id not in m_ids,说明创建ReadView时生成该版本的事务已经被提交,该版本可以被访问。
  • 如果某个版本的数据对当前事务不可见,那就顺着版本链找到下一个版本的数据,并继续执行上面的步骤来判断记录的可见性,以此类推,直到版本链中的最后一个版本。

下面来测试一下以上理论:

ReadView生成的时机

  • READ COMMITTED和REPEATABLE READ隔离级别之间一个非常大的区别就是——它们生成ReadView的时机不同!!
  • READ COMMITTED——在一个事务中,每次读取数据前都生成一个ReadView。
  • REPEATABLE READ——在一个事务中,只在第一次读取数据时生成一个ReadView。

  • 两种情况下生成一个ReadView:
    • 情况①:RW的ids为【0,0】
    • 情况②:RW的ids为【10,0】
    • 情况③:RW的ids为【20】
    • 情况④:RW的ids为【】

每次读取数据前都生成一个ReadView,情况③时select操作,则由于已提交的trx_id=10<20,因此可以被看见,即发生了不可重复读的现象;而只在第一次读取数据时生成一个ReadView,由于事务1,2最开始读取的数据对应trx_id=2,小于10,因此即使此时去做select操作,也不会发生不可重复读的问题,这是可重复读的隔离级别的必然结果。

MVCC总结

MVCC是利用记录的版本链和ReadView,来控制并发事务访问相同记录时的行为。版本链就是roll_pointer连接的一条链表,RW可理解为事务id列表的几种id。MySQL就是通过RW的几种id与当前事务id作比较来判断目前事务id访问的版本是否可见,版本可见的情况包括:当前id小于、等于、不在RW版本链中,以及因为坚持回溯版本链最终到可见的地步,共四种情况。每次读取数据前都生成一个ReadView与只在第一次读取数据时生成一个ReadView分别对应了对已提交和可重复读的隔离级别。

### MVCCReadView 的相关概念与实现细节 #### 什么是 ReadView? 在多版本并发控制(MVCC, Multi-Version Concurrency Control)机制下,ReadView 是一种用于管理事务隔离级别的数据结构。它定义了一个事务能够看到哪些数据版本的规则集。当一个新事务启动时,数据库会为其创建一个 ReadView,这个视图包含了当前活跃事务的信息以及其他必要的元数据[^1]。 具体来说,ReadView 主要记录以下几个关键属性: - **m_ids**: 当前系统中所有未提交事务的 ID 列表。 - **min_trx_id**: 所有已提交事务中最小的一个事务 ID。 - **max_trx_id**: 下一个即将分配给新事务的最大事务 ID。 - **creator_trx_id**: 创建此 ReadView 的事务本身的事务 ID。 通过这些字段组合起来决定某个特定的数据版本是否可以被当前读取操作所见[^3]。 #### ReadView 的作用 ReadView 的主要目的是为了支持可重复读 (Repeatable Read) 和一致性读 (Consistent Read),从而保证在一个事务内的多次 SELECT 查询都能获得相同的结果集,即便其他并行运行的写入/更新事务已经修改了底层数据也不会影响到本事务感知的内容稳定性[^1]。 例如,在 InnoDB 存储引擎下的 MySQL 数据库中实现了基于时间戳和 Undo Log 的 MVCC 方案,其中就广泛运用到了类似的 ReadView 结构来帮助判断每条记录的历史版本对于当前执行 select 操作所属的那个逻辑快照而言应该是可见还是隐藏状态[^2]。 #### 实现细节分析 以下是关于如何利用 ReadView 来判定一条记录是否对当前事务可见的具体流程描述: 1. 如果目标记录的 `trx_id` 小于 min_trx_id,则表示这条记录是在任何现有事务之前就已经完成的操作结果,因此它是完全可视化的; 2. 若该记录的 `trx_id` 大于 max_trx_id ,则意味着这是未来才会发生的事件所产生的变更效果,自然也就不可见; 3. 对于介于两者之间的那些情况,则需进一步检查 m_ids 数组 。只有当且仅当生成这条记录的那个事务并不处于列表当中或者说它的状态已经是 COMMITTED 而非 PENDING 状态的时候我们才认为它可以展示出来供本次查询使用[^3]。 ```sql SELECT * FROM table_name WHERE condition; -- 上述 SQL 在执行过程中会依赖 ReadView 来确定返回哪一版的数据副本。 ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值