MySQL-MVCC

一、阐述

mvcc(multi version concurrency control)多版本并发控制,其作用就是在让特定隔离级别的事务在并发时,保证在事务中能实现一致性读(无法保证写一致性),虽然锁可以控制并发操作,但是锁的系统开销比较大,而mvcc可以在大多数情况下替代行级锁,同时可以降低系统开销。

二、原理

对于Innodb存储引擎来说,每个聚簇索引记录中都包含两个隐藏列:

1)trx_id:每当一个事务对记录进行修改时,都会将该事务的id记录在这个列里。

2)roll_pointer:每当一个事务对记录进行修改时,都会把旧版本写入undo日志中,这个隐藏列的作用就是指向此记录修改前的信息。

示例1.jpg

每次修改记录,都会向undo日志增加一条记录,每条undo记录都都有一个roll_pointer属性(insert操作对应的undo日志没有该属性),用以将这些undo日志串成一个链表。如下:

两个事务都对id=1的记录进行修改。

示例2.jpg

这些undo日志将会串成一个链表,如下:

示例3.png

对此记录每次修改后,都会将旧值作为一条记录放入undo日志中,随着操作的累加,通过roll_pointer就会形成一个链表,即版本链,这个版本链的头结点就是此记录的最新值。

insert undo 在事务提交后就会删除,因为它初始化后本身就在版本链头结点,所以继续存储在undo日志中没有必要,但是update undo为了继续支持mvcc,所以继续存在于undo日志中。

为了支持MVCC,对于delete mark操作来说,仅仅是在记录上打一个删除标记,并没有真正将它删除掉。

在接下来的时间,MySQL系统会判断哪些undo日志不会再被访问了,后台运行的purge线程会把它们删除。

三、ReadView

作用:通过ReadView可以找到当前事务能访问的记录。

两种隔离级别不需要ReadView:

1)READ UNCOMMITTED读未提交,每次都获取最新的值就行了,不需要在版本链里查询符合自己条件的旧数据。

2)SERIALIZABLE 串行化,InnoDB存储引擎,为SERIALIZABLE隔离级别提供加锁的访问方式(读写锁),只有一个事务结束,另外一个事务才可以进行操作。

两种隔离级别需要ReadView:

READ COMMITTEDREPEATABLE READ,都必须保证读到已提交的事务修改的记录。

ReadView有四个属性:

1)m_ids

表示生成ReadView时,当前数据库系统中活跃的读写事务id列表。

2)min_trx_id

表示在生成ReadView时,当前数据库系统中活跃的读写事务中事务id最小的事务,对应m_ids中的最小值。

3)max_trx_id

表示生成ReadView时,数据库系统准备给下一个事务分配的id

4)creator_trx_id

表示生成ReadView的事务id

通过ReadView判断某个版本记录是否对当前事务可见

在讲如何判断之前,我们一定要知道事务id是什么时候分配的。

在《MySQL事务隔离级别》这篇文章里有提到过。不熟悉的请去翻一翻,否则,在下面讲解过程中容易翻车。

判断:

1)如果被访问记录的事务id(trx_id)与ReadView中的creator_trx_id相同,意味着当前的事务正在访问被它修改过的记录,所以该版本的记录可以被当前事务访问。

2)如果被访问记录的事务id(trx_id)小于ReadView中的min_trx_id,表明生成该记录的事务在生成ReadView时已经提交,所以该版本的记录可以被当前事务访问。

3)如果被访问记录的事务id(trx_id)大于或等于ReadView中的max_trx_id,表明生成该记录的事务在生成ReadView的事务之后,才开启(创建)的,所以该版本的记录不能被当前事务访问。

4)如果被访问记录的事务id(trx_id)在min_trx_id 和 max_trx_id之间,需要判断trx_id是否在m_ids列表中,如果在,说明生成ReadView时,trx_id对应的事务是活跃的,该版本的记录不可被当前事务访问,如果不存在,说明创建ReadView时,trx_id对应的事务已经提交,该版本的记录可以被当前事务访问。

如果某个版本的记录不可以被当前事务访问,MySQL会沿着undo版本链一直向下查找,直到最后一条数据,如果最后一条记录也不可以被当前事务访问,那就说明此记录对该事务完全不可见,查询结果就不会包含这条记录。

ReadView的生成时机

上面介绍了MySQL只有在READ COMMITTEDREPEATABLE READ两种隔离级别下,才会生成,但是这两种隔离级别生成ReadView的时机是不同的:

1)READ COMMITTED在每次查询时都会生成ReadView。

2)REPEATABLE READ在第一次查询时生成ReadView,之后在这个事务内的所有查询操作,不再生成ReadView。

四、讲解

1)READ COMMITTED 隔离级别

示例4.png

2)REPEATABLE READ 隔离级别

示例5.png

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容