MySql MVCC 机制
在说MVCC机制前我们先了解ReadView。
ReadView是什么呢?在我们平时执行一个事务的时候,就会生成一个ReadView,ReadView的组成结构大致如下
参数说明:
creator_trx_id:当前事务id
m_ids:所有事务的事务id
min_trx_id:m_ids里最小的事务id值
max_trx_id:最大事务id
我们来具体分析一下ReadView的作用,以及是如何解决脏读幻读,不可重复读的问题的
现在数据有一条数据,如下原始值,上一个已经提交事务的事务txr_id=30
此时两个事务并发执行, 事务A(txr_id= 40),事务B(txr_id=50),事务A要读取这行数据,事务B要更新这行数据。
此时事务A在读取时开启ReadView,此时的ReadView的值如下:
m_ids:=[40,50]
min_trx_id=40
max_trx_id=51
creator_trx_id=40
事务A在第一次查询发起一个判断,判断当前数据的txr_id是否小于ReadView中的min_trx_id(此时txr_id (30)< min_trx_id(40)),说明在A事务开启前,修改这行数据的事务已经提交了,所以可以查询这条数据
接着事务B开始修改数据为B值,然后将这行数据的txr_id设置为自己的txr_id(50),然后将roll_pointer指向之修改前生成的undo log备份,然后提交事务
此时A再重复查询,当读取B修改的数据时,发现在ReadView中min_txr_id(40)<txr_id(50)<max_trx_id(51),同时txr_id=50在m_ids,可以确定修改数据的事务和自己处于同一时间并发提交的,为了保证可重复读,这行数据是不能查询的,所以需要通过roll_pointer顺着undo log链表找到最近的一条undo log,即修改前的值,找到txr_id = 30(B未修改的值) 发现此时原始值的 txr_id < min_trx_id,说明这条数据肯定是在事务A开启钱前提交的,所以可以查询,就查询txr_id=30的值,即查询的的还是原始的值,这样就保证了可重复读
如果此时的数据隔离级别是Read committed,即B事务一旦提交,A下次查询就可以马上查询到事务B提交的值了,ReadView是如何做到这一点的呢?
那就是每次事务A查询时都新生成ReadView,来看看在事务B提交后A重新查询后生成的ReadView是什么样的
这次A重复查询在ReadView中 min_txr_id(40)<txr_id(50)<max_trx_id(41),但是事务B的事务id不在m_ids中,说明事务B虽然和自己同一时间开启,但是已经提交了事务,所以可以查询B修改的数据即txr_id = 50的值
接着事务A再更新这行数据,此时这行数据的trx_id=40,此时数据如下
此时事务A重新查询,发现txr_id=40 = creator_trx_id,说明这条数据就是自己修改的,也是可以读取到,所以就读取到
如果此时再开启一个事务C,事务id=60,更新这行数据
如果A再次查询会发现trx_id=60 > max_trx_id(id=51),说明事务C是事务A开启后开启的,自己也是不能读取到,所以也是顺着undo log向下找到 txr_id = 40 读取到值
同理如果事务隔离级别是 Repeatable read,C事务插入一条数据也是如此,A是读取不到的,从而也解决了幻读问题