图解MVCC
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。