mvcc巨无霸【重点】
1 ru rc
xxx val1 sac9 | rc | ru | |
start sac13 | |||
xxx val2 sac13 undo1 xxx val1 sac9 |
update | ||
select start sac18 |
|||
readview [13,18] min 13 next 19 create 13 |
直接返回val2 | ||
sac13 >= 13 < 19 &&sac13 属于[13,18] 证明该记录未提交,不可访问 |
|||
查undo1 |
|||
sac9 < 13 证明该记录已提交,可访问 返回val1 |
2 rc rr
xxx val1 sac9 | rc | rr | |
select start sac11 (为了保证事务内可见性,同一个事务多次select,一个事务id) |
|||
readview [11] min 11 next 12 create 11 |
|||
sac9 < 13 证明该记录已提交 返回val1 |
|||
start sac13 | |||
xxx val2 sac13 undo1 xxx val1 sac9 |
update | ||
commit 13 | |||
readview [11] min 11 next 14 create 11 |
沿用readview readview [11] min 11 next 12 create 11 |
||
sac13 >= 11 < 14 &&sac13 不属于[11] 证明该记录已提交,可访问 |
sac13 > 12 该记录事务在当前事务生成 |
||
返回val2 | 查undo1 | ||
sac9 < 11 证明该记录已提交,可访问 返回val1 |
|||
以下为undo删除 | |||
当前事务集合[11] next sac 14 |
|
||
undo0 sac13 > 11 该记录未沉默,仍对一部分事务不可见(比一部分事务readview最小值大) 它仍有传递链表节点的责任和价值 |
|||
commit 11 | |||
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博客