MVCC全称是 多版本并发控制(Multi-Version Concurrency Control),
mysql中实现可重复读隔离级别下的快照读就是依赖了这项技术,本文将通过大量图例以一种简明的视角讲清楚这个概念。

存储

应用MVCC需要在数据库每条记录上增加如下两个字段

  • 事务ID,即创建或修改记录的事务ID,以下统称为TRX_ID
  • 回滚指针ROLL_PTR,指向该行之前的版本,如果刚创建,该指针为空

假设记录在事务1中被创建和修改。可以看到有两个版本的记录,新版本的roll_ptr指向旧版本。

然后在事务2中修改记录,同样最新记录的roll_ptr指向修改前的记录。

查询

仅保存记录的不同版本还不够,还需要在事务中记录其他还未结束的事务,以保证在当前事务中进行查询

  • 可以读取到最新的已提交事务创建或修改的记录
  • 不会读取到未提交事务的更新

这里用到了一个ReadView的结构,由事务在第一次select查询时创建(可重复读),简单来说就是一个的活跃(未提交)事务的列表。
记列表中最大id为max_id,最小为min_id,则有

  • 小于min_id的事务均已提交,所有修改均可见
  • 大于max_id的事务均未提交,所有修改均不可见
  • 在min_id和max_id之间的事务
    • 如果在活跃事务列表中,未提交,修改不可见
    • 否则已提交,修改可见

按照上述规则查询,最新版本的记录不符合,则通过roll_ptr找上一个版本,直到找到符合的版本,或没有匹配记录时返回。
可重复读级别下,只会在事务的第一个select查询时创建ReadView,而对于读已提交,在每次查询时都会创建ReadView。

例子

假设数据库中已经有一条记录id=1,我们启动了三个事务,t1修改记录,t2和t3查询,不同的是t2是可重复读,而t3是读已提交

在时刻0,记录的快照和t2、t3的ReadView如下

由于t1还没提交,所以t2和t3查询到的名称都是alice。
到了时刻2,t1已经提交。由于可重复读不会更新ReadView,t2查询到的名称仍是alice,但是t3更新了ReadView,可以查到最新的名称bob。

Categories: ,

Updated: