案例二
本例要求知道 select的第二个查询结果。深黑色字体。
同样是在kaka那一条记录的基础上。
当事务ID100两次更新后,版本链也会改变,现在的版本链如下图,红色部分为最新数据,蓝色数据为undo log的版本链数据。
对于此时生成的read-view你会有什么疑问,在RR级别也就是可重复读的隔离级别下。
当在一个事务下执行查询时,所有的read-view都是沿用的第一条查询语句生成的。
那此时的read-view也就是[100,101],102
看一下底层查找步骤
目前数据的事务ID为100
根据规则会落在min_id<=trx_id<=max_id这个区间
并且当前行的事务ID100是在read-view的数组中的,表示此时事务还没有提交则不可见
继续在版本链中往下寻找,此时找到的事务ID还是100,跟上述流程一致
通过查找版本链,将发现事务 ID为102
102是read-view的max_id,同样也会落在min_id<=trx_id<=max_id这个区间,但是跟之前不同的是事务102是没有在数组中的,表示这个版本事务已经提交了所以是可见的
最后返回的是 niuniu
案例三
为了让大家体验一下可重复读级别生成的read-view是根据在同一事务中第一条快照读产生的,再来看一个案例
此时的事务ID101也再对数据更新两次,然后在进行查询看一下会返回什么值
经过案例一、案例二的熟悉现在对undo log的版本链和对比规则已经有了一定的了解了吧!
案例三就不在那么详细的说明了。
此时的版本链如下
此时的read-view依然为[100,101],102。
那么首先会根据事务101去版本链对比,事务101和事务100都会落在min_id<=trx_id<=max_id这个区间,并且还都在数组中,所以数据是不可见的。
那么继续往版本链中寻找就会遇到事务102,这个是最大的事务ID并且不在数组中,所以是可见的。
于是最终的返回结果还是niuniu。
案例四
可以看到个案例三的图不同的是新增了一个查询语句,那么假设这俩条语句执行的时间都是一致的,它们返回的结果会相同吗?
案例三查询到的值为niuniu
其实现在版本链跟案例三也是一致的
那么来梳理一下寻找过程
- 首先这里的read-view发生了变化,此时的read-view为[101],102
- 拿着当前的事务ID101跟版本链规则进行对比,落盘在min_id<=trx_id<=max_id,并且在数组中,则数据不可见
- 然后进入版本链,找到下一个数据的事务 ID,还是101,与上一个一致
- 接下来是事务ID100
- 事务ID100是落在trx_id<min_id,表示此版本是已经提交的事务生成的,由于事务已经提交所以数据是可见的
- 所以最终返回结果为niuniu2
小结
在同一个事务中进行查询,会沿用第一次查询语句生成的read-view(前提是隔离级别是在可重复读)
通过以上的四个案例,在版本链寻找过程中,可以总结出一个小技巧
根据这个小技巧你可以很快的得知此版本是否可见。
如果当前的事务ID在绿色部分,是已经提交事务,则数据可见
如果当前的事务ID在蓝色部分,会有俩种情况,如果当前事务ID在read-view数组内,是没有提交的事务不可见,如果不在数组内数据可见
如果落在红色部分,则不考虑,对于未来的事情不去想即可。
七、总结
阅读本文后,在面试过程中极大可能会遇到的问题就是聊聊你对mvcc的认识。
本文内容从浅到深,从什么是mvcc到mvcc的底层实现,一步一步地陈述了mvcc的实现原理。
本文简单总结
mvcc在不加锁的情况下解决了脏读、不可重复读和快照读下的幻读问题,一定不要认为幻读完全是mvcc解决的
对当前读、快照读理解,简单点说加锁就是当前读,不加锁的就是快照读。
mvcc实现的三大要素俩个隐式字段、回滚日志、read-view
俩个隐式字段:DB_TRX_ID:记录创建这条记录最后一次修改该记录的事务ID,DB_ROLL_PTR:回滚指针,指向这条记录的上一个版本
undo log在更新数据时会产生版本链,是read-view获取数据的前提
read-view当SQL执行查询语句时产生的,是由为提交的事务ID组成的数组和创建的最大事务ID组成的
版本链规则看第六节的小结即可