Mysql MVCC机制讲解

一 概述

在MySQL InnoDB存储引擎下

RC(读已提交)、RR(可重复读)基于MVCC(多版本并发控制)进行并发事务控制

MVCC是基于”数据版本”对并发事务进行访问

![image](images/2-n1nPUh3DurpVWW_aKrVOyrpaW8paCoRrJb2dDnHWM.png)

从上图我们来解读:

如果在RC级别下:事物D在第一次查询得出的是张三,第二次读取的时候,由于第二次提交已经生效,所以读取出来的是张小三,出现了不可重复读

在RR级别下:事务D两次读取都是张三

接下来我们看上述操作,mysql底层都记录了什么:

mysql底层针对上述操作,写入了一份给予UNDO_LOG的日志链:

![image](images/4WGwj8iw7FzeObWU-0-CJZW_fRCFM6Gi5TmNrUoy4c4.png)

日志链的最上层是最后数据库落盘的数据,即最后提交的张老三,事务编号为3,最后的字段DB_ROLL_PTR是指针信息,指向了上一次记录的数据信息。

疑问:undo_log不实在事务回滚之后就会删除吗?如果食物编号2回滚后,岂不是链表就断掉了?

UNDO_LOG版本链不是立即删除,MySQL确保版本链数据不再被“引用”后再进行删除。可以参考jvm的可达性分析以及三色标级原理去理解。

那么日志链到底有什么作用呢?我们引入一个概念:ReadView。

什么是快照读?

快照读其实就是指的常规的select语句,readView就是快照读提取MVCC数据的依据。

与其对应的,还有一个当前读,是执行下列语句时进行的数据读取方式:

Insert、Update、Delete、

Select...for update

Select...lock in share mode

readView的数据结构组成:

  1. m_ids:当前活跃的事务编号集合
  2. 1min_trx_id:最小活跃事务编号
  3. max_trx_id:预分配事务编号,当前最大事务编号+1
  4. creator_trx_id:ReadView创建者的事务编号

readView不同的场景:

RC:每一次执行快照读时生成readView,如图:

![image](images/kJajQOyyDUZ8xQVtNMRt9S5jkNKvnqCP38iHEOPvACY.png)

在执行快照读语句的时候生成了对应的readView。

m_id:2,3,4 为什么没有1? 因为1已经提交了,不再活跃

min_trx_id: 活跃最小id,2

max_trx_id: 预分配事务id,5

creator_trx_id: 创建此快照的事务id,4

第二条select语句生成的readView如上理解。

我们来看一下生成readView之后的数据访问逻辑:

![image](images/kwtr65Etlgi73EadSOpAcQeXVnskxX0jZ0VZ2LEi3fM.png)

![image](images/MOuxVQeHQ2zoTWqDbwDeSwo888XyL_zv3A33lbhZV0k.png)

上面的所有过程其实总结就一句话:从最新的事物开始判断,是否是不活跃的,即已经提交的,只有已经提交的数据可以访问,所以当前第一次select只有针对trx_id = 1的那条数据才具备访问的权限,所以返回的就是张三。

可重复读生成的readView的情况:

仅在第一次执行快照读时生成ReadView,后续快照读复用

(有例外:后面会说)

![image](images/paumPmUmhCwrt8jRJl_UUJpOQU8x-e5hoM8ff0CGWaE.png)

RR情况下使用MVCC可以解决幻读吗?

能,但不完全能!

原因:当在事务的过程中产生了新增,删除操作,对于快照读是不可见的,即当两次快照读中间插入了当前读,即会产生幻读。

![image](images/LqkzPqrwk-LXNTBzQEr_5u2RcX8MWZtsdE-TiH86B5E.png)

Last modification:July 5, 2022
If you think my article is useful to you, please feel free to appreciate