mvcc巨无霸【重点】

1 ru rc

 

xxx val1 sac9   rc ru
  start sac13    

xxx val2 sac13

undo1 xxx val1 sac9

update    
   

select

start sac18 0

 
   

readview

{13, 18 0}

min 13 0

next(max) 19(假设有其他线程)

create 13 0

直接返回val2
   

sac13 >= 13 < 19 属于[0,19)

&&sac13 属于{13,18}

&&属于{13,0}

证明该记录val2未提交,不可访问

 
   

查undo1

 
   

sac9 < 13 属于[0,19)

但不属于{13,0}

证明该记录已提交,可访问

返回val1

 

 

2 rc rr

xxx val1 sac9

undo1 xxx val1 sac6

  rc + rc rr
   

select

start sac11 0

为了保证事务内可见性,同一个事务多次select,一个事务id

 
   

readview

{0}

min 11 0

next 12

create 11 0

 
   

sac9 < 13 11  

当前readview集合为空,min没有-》无强大,而9<无穷大,证明该记录已提交

9属于[0,12)

但不属于{0},证明该记录已提交

返回val1

 
  start sac13    

xxx val2 sac13

undo1 xxx val1 sac9

update  rc 再次查询  rr 再次查询
  commit 13    
   

readview

[11] {0}

min 11 0

next 14

create 11 0

沿用readview

readview

[11] {0}

min 11 0

next 12

create 11 0

   

sac13 >= 11 属于[0,14)

&&sac13 不属于[11] {0}

证明该记录已提交,可访问

 sac13 >= 12

该记录事务在当前事务生成ReadView后才开启,所以该版本不可以被当前事务访问

     返回val2  查undo1
     

 sac9 < 11 属于[0,12)

&&sac9不属于{0}

证明该记录已提交,可访问

返回val1

 以下为undo删除      
目前有2个redo 记录13(B+树)和9(redo)

当前事务集合[11] 当前最小min

next sac 14

最小next 12,而13>=12 

9<12,删除9以下的

当前next14 

当前next12

 undo0 sac13 > 11

该记录未沉默,仍对一部分事务不可见(比一部分事务readview最小值大)

它仍有传递链表节点的责任和价值

    commit 
 

 最小next 12,而13<14 

删除13以下

 14  

 unco0 sac13 < 14, C为空

该记录沉默,对于当前所有事务及将来所有事务,有 sac13 < min sac

该记录对所有其它事务可见

     
 该记录的链表下一个节点undo1失去价值      
 purge线程删除undo1      

 

3 参考:

https://www.cnblogs.com/jmliao/p/13204946.html MVCC ReadView介绍

对于使用READ UNCOMMITTED隔离级别的事务来说,由于可以读到未提交事务修改过的记录,所以直接读取记录的最新版本就好了;对于使用SERIALIZABLE隔离级别的事务来说,设计InnoDB的大叔规定使用加锁的方式来访问记录(加锁是啥我们后续文章中说哈);
对于使用READ COMMITTED和REPEATABLE READ隔离级别的事务来说,都必须保证读到已经提交了的事务修改过的记录,也就是说假如另一个事务已经修改了记录但是尚未提交,是不能直接读取最新版本的记录的,
核心问题就是:需要判断一下版本链中的哪个版本是当前事务可见的。为此,设计InnoDB的大叔提出了一个ReadView的概念,这个ReadView中主要包含4个比较重要的内容:

m_ids:表示在生成ReadView时当前系统中活跃的读写事务的事务id列表。

min_trx_id:表示在生成ReadView时当前系统中活跃的读写事务中最小的事务id,也就是m_ids中的最小值。

max_trx_id:表示生成ReadView时系统中应该分配给下一个事务的id值。

小贴士: 注意max_trx_id并不是m_ids中的最大值,事务id是递增分配的。比方说现在有id为1,2,3这三个事务,之后id为3的事务提交了。那么一个新的读事务在生成ReadView时,m_ids就包括1和2,min_trx_id的值就是1,max_trx_id的值就是4。

creator_trx_id:表示生成该ReadView的事务的事务id。

小贴士: 我们前边说过,只有在对表中的记录做改动时(执行INSERT、DELETE、UPDATE这些语句时)才会为事务分配事务id,否则在一个只读事务中的事务id值都默认为0。胡说八道,为0还怎么搞mvcc多版本读?

有了这个ReadView,这样在访问某条记录时,只需要按照下边的步骤判断记录的某个版本是否可见:

如果被访问版本的trx_id属性值与ReadView中的creator_trx_id值相同,意味着当前事务在访问它自己修改过的记录,所以该版本可以被当前事务访问。
如果被访问版本的trx_id属性值小于ReadView中的min_trx_id值,表明生成该版本的事务在当前事务生成ReadView前已经提交,所以该版本可以被当前事务访问。
如果被访问版本的trx_id属性值大于或等于ReadView中的max_trx_id值,表明生成该版本的事务在当前事务生成ReadView后才开启,所以该版本不可以被当前事务访问。
如果被访问版本的trx_id属性值在ReadView的min_trx_id和max_trx_id之间,那就需要判断一下trx_id属性值是不是在m_ids列表中,如果在,说明创建ReadView时生成该版本的事务还是活跃的,该版本不可以被访问;如果不在,说明创建ReadView时生成该版本的事务已经被提交,该版本可以被访问。
如果某个版本的数据对当前事务不可见的话,那就顺着版本链找到下一个版本的数据,继续按照上边的步骤判断可见性,依此类推,直到版本链中的最后一个版本。如果最后一个版本也不可见的话,那么就意味着该条记录对该事务完全不可见,查询结果就不包含该记录。

在MySQL中,READ COMMITTED和REPEATABLE READ隔离级别的的一个非常大的区别就是它们生成ReadView的时机不同。

READ COMMITTED —— 每次读取数据前都生成一个ReadView

REPEATABLE READ —— 在第一次读取数据时生成一个ReadView

 

 

https://zhuanlan.zhihu.com/p/166152616 MySQL 的可重复读到底是怎么实现的?图解 ReadView 机制

https://blog.csdn.net/longgeqiaojie304/article/details/98872857 Mysql隔离性之Read View MVCC ReadView介绍

 

 

4 undo在内存中,什么时候删除?

4.1 insert

commit后直接删除

4.2 update

设当前活跃事务集合C,其最小值为cmin,nextsac

当undo sac < cmin,或当C为空且undo sac < nextsac时,undo sac沉默,对将来所有事务可见,则undo sac链表下级 undo sac1可删除

 

 

 

 

Mysql Innodb中undo-log和MVCC多版本一致性读 的实现 - chouhe8007 - CSDN博客

史上最详尽,一文讲透 MVCC 实现原理 - 居士的CSDN - CSDN博客

(11条消息)mysql innodb中MVCC理解和redo,undo log详解 - harryptter的专栏 - CSDN博客

(11条消息)【MySQL】InnoDB 中 RC、RR 事务隔离级别下快照读的区别及原因 - chenghan_yang的博客 - CSDN博客

(12条消息)轻松理解MYSQL MVCC 实现机制 - 杨龙飞的博客 - CSDN博客

Mysql心路历程:两个"log"引发的"血案" - 心中的理想乡的个人页面 - OSCHINA

posted on 2020-10-13 14:03  silyvin  阅读(136)  评论(0)    收藏  举报