mysql-MVCC机制及其内部执行原理

目录

MVCC简介

undo日志版本链与read view机制详解

可重复读隔离级别下的mvcc机制的运行原理

读已提交隔离级别下的mvcc机制的运行原理


MVCC简介

MVCC全称为Multi-Version Concurrency Control,多版本并发控制。

当事务的隔离级别设置为‘读已提交’或者‘可重复读’的时候,select语句实际是是通过mvcc机制进行维护的,不对其加锁。而数据库的大多操作都是查询,这无形中就提高了并发度(select 操作是快照读,读历史版本,增删改操作是当前读,读当前版本)。


undo日志版本链与read view机制详解

在讲解MVCC机制之前要先提到两个息息相关的概念

undo日志版本链:一行数据被多个事务一次修改后,在每个事务修改完成,Mysql会保留修改前的undo回滚日志,并且用两个隐藏字段trx_id和roll_pointer把这些undo日志串联起来形成一个历史版本链。

 read view:mysql执行start transaction实际是并不是事务真正的开启时间,事务真正的开始时间实际是是执行第一条sql语句的时候。当事务中执行第一条查询sql语句的时候,会生产一个针对当前事务的一致性视图(read view),也就是说每一个事务都有各自的一致性视图,这个一致性视图会一直存在并且不会改变直到事务提交或回滚。

一致性视图是由两部分组成

  • 这个视图由执行查询时所有未提交事务id数组(数组里最小的id为min_id)
  • 已创建的最大事务id(max_id)组

顺带提一下,事务id的生成时机是在事务中第一条增删改语句执行的时候产生的,如果事务一开始就执行的是select语句,实际是是不会生成事务id的,而这个事务id与undo日志中的txr_id时对应的,只有在执行增删改操作才会记录到undo日志中,并且事务id时递增的,例如第一个事务的事务id为1,那么第二个事务的事务id肯定会大于1

可重复读隔离级别下的mvcc机制的运行原理

 我们知道 一致性视图由两部分组成,一个是正在活动的事务id数组,一个是当前最大的事务id,而通过id数组中的最下min_id和当前最大的max_id可以划分三个概念。在min_id之前的都是已经提交了的事务,而在max_id之后的都是还没开始的事务,而位于min_id和max_id之间的是未提交或者已经提交了的事务(为什么大于min_id会有已提交的事务呢?因为可能第201的事务刚创建执行了一条sql之后就提交了,生命周期较短)。




现在有四个事务如下图所示

 事务id为100的事务先执行了一条sql,获取事务id,事务id为200的数据也是执行了一条sql获取事务id。由于这两个事务前面执行的不是我们要讲的表,就不多做解释。

事务id为300的执行了一条sql语句,修改了account表id为1的name,并获取的事务id。

而在事务id为300的事务提交之后,第四个事务进行了查询操作(查询不分配事务id),创建了readview(其他的事务也有各自的readview,这里针对第四个事务进行解释),事务id为100和事务id为200的事务都没有提交,所以保存在readview的id数组中,其中100为最小id。事务id为300的事务已经提交,不进入数组中,但是300是目前最大的事务id,所以记录下来。

当第四个事务要去执行第一条select语句时,就会去undo日志中去查找

 此时undo日志中关于这条行数据的修改只有一个版本,就是事务id为300的事务将name修改为lilei300,此时就会拿这一行修改记录的id去readview去进行匹配。

匹配原则

  1. 如果id<min_id就代表事务是已提交的,这个结果可以使用
  2. 如果id>max_id就代表这是未来的事务执行的,结果不可用
  3. 如果id在是min_id 和max_id或者在他们之间,就有两种可能。判断id是否在readview的id数组中,如果在就表示执行这一行操作的事务还未提交,结果不可用(但是如果这个id是当前查询的事务本身,就可用结果)。如果id不在readview的id数组中,就表示事务已经提交,结果可用。

这个时候我们可用看到执行修改name为liilei300的事务id为300,这时候符合匹配原则的第三条,需要和readview的id数组进行匹配。此时id数组为【100,200】,300并不在里面。说明事务id为300的事务已经提交,结果是可用的,所以查询到的数据就是这一条。


而接下来第一个事务又去修改了数据,先后将name修改为lilei1,和lilei2,然后第四个事务又去查询了一次

 

 此时又会去undo日志中去找可用结果,当找到第一条的时候,先拿出id去判断,发现id是符合匹配原则的第三条,于是这时候要和readview中的id数组进行比较,发现100在id数组中,所以结果不可用,往下一条也是id为100,结果还是不可用,再往下找到id为300的,也是符合匹配原则的第三条,这时候去readview的id数组中匹配,发现300不在数组中,结果可用,于是返回了这一条结果


此时第一个事务提交,第二个事务也对name做了两次修改,先后改成liilei3和lilei4,然后第四个事务又去查询了一次

 

 值得提一点的是,就算此时第一个事务已经提交了,但是之前有提过,当readview生成之后就会伴随着一个事务不改变,直到事务提交。所以此时事务4的readview还是由【100,200】,300组成(未提交id数组以及最大id)

又回到undo日志去查找可用结果,第一条id为200,符合匹配原则第三条,然后与readview的id数组匹配,发现在id数组中,结果不可用,第二条id还是200,结果一样不可用,而第三条的id为100,虽然说事务id为100的事务已经提交,但是readview一旦生成之后就不可变了,于是这里仍然当作事务id为100的事务结果不可用,第四条也是一样的道理,而第五条事务id为300,去匹配id数组,发现不在里面,于是选择这条结果返回。

读已提交隔离级别下的mvcc机制的运行原理

实际上的运行原理,匹配原则其实是一样的,唯一不同的就是在读已提交隔离级别下,每执行一条sql语句就会重新生成readview,而不可重复读的readview一旦生成就不改变。

正因为每执行一条sql就重新生成readview,其id数组中的数据永远是最新的,当一个事务提交了之后,id就会从id数组中移除,这样那一行改变的数据就变成了可用结果,所以读取到的都是最新提交的数据。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值