当前位置:网站首页>快照读下mvcc实现避免幻读

快照读下mvcc实现避免幻读

2022-08-11 05:21:00 小孩来了

在上一篇文章中写了在事务隔离级别为RR下关于gap间隙锁的演示,而RR这种隔离级别也正是通过间隙锁和临键锁避免了在当前读下避免了幻读的产生,但是在快照读的情况下完全可以通过mvcc+readView+undo 事务版本链来避免幻读,下面我们就介绍下其原理。

undo 事务版本链

在undo log日志里,每条数据除了自有的那些字段(表id、日志类型、数据页号等等),其实还会有两个隐藏字段,一个是trx_id,另一个是roll_pointer。这个trx_id就是最近一次更新的事务id,roll_pointer是指向你更新这个事务之前生成的undo log数据。

以下的案例都是在RR隔离级别下进行,RC级别下不管是快照读和当前读都会更新readview所以每次查询的数据不一致,所以在RC下演示没有意义
 

         开启事务A插入一条数据作为初始数据

由于在事务A之前没有其它的事务 所以roll_pointer指向的就是null

接下来我们在开启事务B并插入数据这时候在undolog中和事务A就会形成一个版本链,如果后面还有多个事务,以此类推

readView

     可以理解为在快照读下mvcc查询展示undo事务版本链中具体版本数据的依据。

      在RR级别下无论发生多少次快照读只会在第一次快照读的时候生成readView,如果发发生当前读该readView是不会发生变化,这里主要关注readView中以下属性:

    1.m_ids:记录有哪些事务在mysql里还没有提交。
    2.min_trx_id:m_ids里的最小值。
    3.max_trx_id:下一个mysql要生成的事务id。
    4.creator_trx_id:就是你这个事务本身的事务id。

下面通过案例演示说明,mvcc怎么通过readView在undo事务版本链中找到需要展示的数据的依据

案例演示

先大概说下mvcc根据readView查询需要展示的事务版本规则

1,版本链中的trx_id 小于 min_trx_id 可以查看

2,版本链中的   min_trx_id<trx_id < max_trx_id  &&  不在 m_ids中的可以查看

3,版本链中的trx_id=creator_trx_id 可以查看

开启事务A

   我们在只有两个字段(id,age)的表user中插入一条数据(1,20)并提交事务,我们姑且改事务A的trx_id=20。

开启事务B trx_id=30

执行select *  fron  user  

接下来观察一下readView和undo事务版本链

mvcc会根据dudo版本链中的trx_id 去readView中做对比,判断该事务版本是否可以被查看

很明显 20<30  说明事务A是在事务B之前提交的可以被查看

开启事务C trx_id=40

执行

insert Into user values(2,30)--提交事务

这时再去事务A中执行select  *  from  user

虽然在事务C中插入了数据并提交了事务,但是在事务A中并没有执行当前读所以readView不变

很明显根据规则,只有事务A可以被查看

所以在这种情况下无论事务B中执行多少次select语句都不会查询出事务C中插入的数据,也就避免了幻读

但是如果在事务A中 执行完快照读后,紧接着,执行insert 和select  最后一次 的select 会把insert数据查出来和第一次select数据不一致产生幻读,对于这种情况RR级别下引入了间隙锁来解决该问题,有兴趣的可以看我上一边gap间隙锁演示

    

   

原网站

版权声明
本文为[小孩来了]所创,转载请带上原文链接,感谢
https://blog.csdn.net/weixin_41638098/article/details/126224569