一、阐述
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 COMMITTED
和REPEATABLE 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 COMMITTED
和REPEATABLE READ
两种隔离级别下,才会生成,但是这两种隔离级别生成ReadView的时机是不同的:
1)READ COMMITTED
在每次查询时都会生成ReadView。
2)REPEATABLE READ
在第一次查询时生成ReadView,之后在这个事务内的所有查询操作,不再生成ReadView。
四、讲解
1)READ COMMITTED
隔离级别
2)REPEATABLE READ
隔离级别
网友评论