Mysql的MVCC---多版本并发控制机制
1、什么是MVCC?
MVCC(Multi-Version Concurrency Control)—多版本并发控制机制,是一种可以避免加锁来控制事务的隔离级别的一种机制,区别于串行化的加锁方式,串行化为了保证超高的隔离性,对于所有的操作都加了锁。
Mysql在读已提交和可重复读隔离级别下都实现了MVCC机制。
2、MVCC的核心概念
2.1、undo日志版本链
undo日志版本链是指一行数据被多个事务依次修改过后,在每个事务修改完后,Mysql会保留修改前的数据undo回滚日志,并且用两个隐藏字段trx_id和roll_pointer把这些undo日志串联起来形成一个历史记录版本链,如下图所示:
2.2、read-view
read-view,也叫一致性视图,在可重复读隔离级别,当事务开启,执行任何查询sql时会生成当前事务的一致性视图read-view,该视图在事务结束 之前都不会变化(如果是读已提交隔离级别在每次执行查询sql时都会重新生成),这个视图由执行查询时所有未提交事务id数组(数组里最小的id为min_id)和已创建的最大事务id(max_id)组成,事务里的任何sql查询结果需要从对应版本链里的最新数据开始逐条跟read-view做比对从而得到最终的快照结果。
生成的read-view如下所示:
-- 100是当前存活事务最小id
-- 200是当前存活事务第二大id
-- 300是当前存活事务最大id
readview: [100,200] 300
3、版本链对比规则
1、如果 row 的 trx_id 落在绿色部分( trx_id<min_id ),表示这个版本是已提交的事务生成的,这个数据是可见的;
2、如果 row 的 trx_id 落在红色部分( trx_id>max_id ),表示这个版本是由将来启动的事务生成的,是不可见的(若 row 的 trx_id 就是当前自己的事务是可见的);
3、如果 row 的 trx_id 落在黄色部分(min_id <=trx_id<= max_id),那就包括两种情况
- a、若 row 的 trx_id 在视图数组中,表示这个版本是由还没提交的事务生成的,不可见(若 row 的 trx_id 就是当前自己的事务是可见的);
- b、若 row 的 trx_id 不在视图数组中,表示这个版本是已经提交了的事务生成的,可见。
4、举栗子
4.1、可重复读
比如上面的这个例子:
1、首先这行数据被写入,写入的事务id为80,如下图所示:
2、第三行、第四行、第五行执行完都会生成一个当前事务唯一的事务id。
3、第八行执行时,会生成当前事务的一个唯一的readView,为 readview:[100,200], 300。在版本链里面进行对比,这时的版本链如下图所示,当我们拿出最上面的300时,300落在黄色区域,根据版本链第三条对比,300不存在于数组中,所以可见,返回lilei300。
4、执行第10行,第11行之后,版本链变成了如下图所示的样子,然后再执行第12行,由于是可重复读隔离级别,所以,readView不变,还是 readview:[100,200], 300,拿出事务id=100,100落在黄色区域,且不是当前事务id,所以,不可见,再拿出100,还是不可见,再拿出300,300落在黄色区域,根据版本链第三条对比,300不存在于数组中,所以可见,返回lilei300。
5、执行十四行,十五行,版本链变成了如下图所示的样子,然后再执行第16行,由于是可重复读隔离级别,所以,readView不变,还是 readview:[100,200], 300,拿出事务id=200,200落在黄色区域,且不是当前事务id,所以,不可见,再拿出200,不可见,拿出100,不可见,拿出100,不可见,再拿出300,300落在黄色区域,根据版本链第三条对比,300不存在于数组中,所以可见,返回lilei300。
6、由此可见,上面就保证了事务的可重复读。
4.2、读已提交
对于读已提交来说,每一次的查询都会生成一个新的readView,就比如说上面的第五步,重新生成一个readView之后,新的readView就是 readview:[200], 200,然后取出来的数据就是lilei2。