【MySQL】基于MVCC的RR、RC级别事务原理简述

我们知道,读已提交(Read Commited, RC)可重复读(Repeatble Read, RR)这两种事务隔离级别,其默认的读的方式,是走的MVCC(Muti Version Concurrent Control, 基于多版本的并发控制,也叫基于一致性的非锁定读)。在RC和RR的级别下的读操作,只有加了lock in share mode(s锁,shared lock,共享锁)和for update(x锁,exclutive lock,排他锁),才走LBCC(Lock Base Concurrent Control,基于锁定的并发控制,也叫基于一致性的锁定读)

在RC和RR的隔离级别下,假设我们开启一个事务,不使用LBCC的锁,在时间点A进行一个查询,过了很久以后,又在时间点B进行一个查询,哪怕这两次查询是select * from table where id = XXX的SQL,这样的SQL会不会慢呢?

答案是在业务上可能成为慢SQL,而且相比其他普通短事务的相同SQL查询,这样长事务中,时间点B(时间靠后)的查询SQL,一定是要更慢的。为什么会这样呢?

这是因为MVCC的多版本中的版本,指的就是undo log。每一次进行写操作,都会生成一个undo log,undo log相当于一个数据快照,并且相比表的记录多了两个字段,roll_ptr,指向上一个undo log(因此形成了一个undo log链表,过期的undo log由mysql的perge线程回收),trx_id,形成这个undo log的事务的id(每一个事务都有自己的id,不同的事务形成的undo log都在同一个undo log链表中,由trx_id区分事务)。

而RR和RC级别的走MVCC的查询,都会根据Read View找到自己可见的版本快照,在undo log链表中找到最近的可见的一个undo log作为查询的返回值。这里需要先解释一下Read View,

**m_ids:**表示在生成ReadView时当前系统中活跃的读写事务的事务id列表。
**min_trx_id:**表示在生成ReadView时当前系统中活跃的读写事务中最小的事务id,也就是m_ids中的最小值。
**max_trx_id:**表示生成ReadView时系统中应该分配给下一个事务的id值。注意max_trx_id并不是m_ids中的最大值,事务id是递增分配的。比方说现在有id为123这三个事务,之后id为3的事务提交了。那么一个新的读事务在生成ReadView时,m_ids就包括12,min_trx_id的值就是1,max_trx_id的值就是4**creator_trx_id:**表示生成该ReadView的事务的事务id。

读已提交,是在每次读取时,都生成一个Read View,这样根据m_ids列表中的活跃事务(这些活跃事务的版本快照,属于未提交的脏数据,不读取),去undo log链表中找到可见的版本快照,这样就避免了脏读问题。

但是因为每次读操作都生成版本快照,存在不可重复读问题。如何产生的呢?比如一个事务A,先查了一次,生成一个Read View,查出相应数据;然后另外一个事务B开启,修改了事务A查询出的数据的值,并且提交了;最后,事务A再次查询,生成一个Read View,注意此时事务B已经关闭,不再活跃,也就是说事务B的版本快照对于事务A是可见的,那么事务A再次查询得到的值,就和一开始查询得到的值不一样了,这就是所谓的不可重复读问题。

解决这个问题的方式就是可重复读级别下,在第一次查询时生成一个Read View,后面的查询不再生成Read View,直接使用第一次查询生成的Read View,确保可见的事务列表不会发生改变,因此避免了不可重复读问题。


注1:ReadView在查看undo log时,会先查看undo log的trx_id是否在min_trx_id和max_trx_id之间。

如果在min_trx_id和max_trx_id之间,再继续看是否在m_ids列表中,如果在m_ids列表中,表示该生成undo log的事务是在生成ReadView时就在活跃的事务,这样的数据属于脏数据,不能读;如果不在m_ids列表中,表示在生成Read View时,生成该undo log的事务不活跃,即已提交,此时该undo log可以正常读取。

如果undo log的trx_id小于min_trx_id,表示生成这个undo log的事务的id,小于生成ReadView时活跃事务的最小id,也就是说它是不活跃事务生成的,即生成ReadView时该事务已提交。数据可以读取。

如果undo log的trx_id大于max_trx_id,表示生成这个undo log的事务的id,大于生成ReadView时活跃事务的最大id,也就是说生成RedView时,生成undo log的事务还没有开启,即生成ReadView后这个事务才开启。这样的undo log不能读,因为你要读的是最近一次符合查询条件的版本数据,不是未来的数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值