

「MySQL高级篇」MySQL之MVCC实现原理&&事务隔离级别的实现 - Melo~
source link: https://www.cnblogs.com/melojun/p/mysql-mvcc.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

大家好,我是melo,一名大三后台练习生,死去的MVCC突然开始拷打我???!
MVCC,非常顺口的一个词,翻译起来却不是特别顺口:多版本并发控制。
- 其中多版本是指什么呢?一条记录的多个版本。
- 并发控制?如何实现呢?我们上篇刚讲到了锁机制,而MVCC则是用更好的方式来提高并发性能,避免加锁!具体如何实现,底层原理是什么,这篇将带你攻破ta。
?本篇速览脑图
通过「版本链」来控制并发事务访问同一个记录时的行为就叫 MVCC(多版本并发控制)。
看完后文,再回过头来看这张图,就会理解了
当前读,快照读
首先我们需要一些前置知识,区分开当前读和快照读。
- 加锁的读,则是当前读,另外update,insert,delete也都是当前读
- 快照读,我们平时简单的select语句其实就是【不加锁】
注意串行化隔离级别下,快照读会退化为当前读。
- 那这俩跟MVCC有什么关系呢?
快照读,相当于你可以读到的是一个历史版本,维护这些历史版本就需要MVCC出马了【其中的undolog版本链】
MVCC用处
解决 读—写 冲突的无锁并发控制,每次对A记录的写操作,都会给A保存一个快照版本,至于读操作的时候,读的是哪个快照版本,这就得看MVCC的实现原理了【下文的readview访问规则】
?MVCC实现原理
?记录中的隐藏字段
InnoDB 里面每个事务有一个唯一的事务 ID,叫作 transaction id。它是在事务开始的时候向 InnoDB 的事务系统申请的,是按申请顺序严格递增的。
而每行数据也都是有多个版本的。每次事务更新数据的时候,都会生成一个新的数据版本,并且把 transaction id 赋值给这个数据版本的事务 ID,记为 row trx_id【也就是下图的DB_TRX_ID】。同时,旧的数据版本要保留,并且在新的数据版本中,能够有信息可以直接拿到它。
-
DB_TRX_ID(6字节):表示最后一次插入或更新该行的事务 id。此外,delete 操作在内部被视为更新,只不过会在记录头 Record header 中的 deleted_flag 字段将其标记为已删除
-
DB_ROLL_PTR(7字节) 回滚指针,指向该行的 undo log 。如果该行未被更新,则为空
-
DB_ROW_ID(6字节):如果没有设置主键且该表没有唯一非空索引时,InnoDB 会使用该 id 来生成聚簇索引
?readview
四个核心字段
计算m_ids的时候,可能会有新的事务产生,为了防止这种情况出现,MySQL保证计算m_ids【也就是生成视图数组的时候】会在事务系统的锁保护下进行,是原子操作,期间不会创建新的事务。
??访问规则
-
如果记录的 trx_id 值小于 Read View 中的 min_trx_id 值,表示这个版本的记录是在创建 Read View 前已经提交的事务生成的,所以该版本的记录对当前事务可见。
-
如果记录的 trx_id 值大于等于 Read View 中的 max_trx_id 值,表示这个版本的记录是在创建 Read View 后才启动的事务生成的,所以该版本的记录对当前事务不可见。
-
如果记录的 trx_id 值在 Read View 的 min_trx_id 和 max_trx_id 之间,表明这个版本的记录在创建 Read View 的时候 可能处于“活动状态”或者“已提交状态”;需要判断 trx_id 是否在 m_ids 列表【活跃状态】中:--【因为是有序的,故采用二分查找】
- 如果记录的 trx_id 在 m_ids 列表中,表示生成该版本记录的活跃事务依然活跃着(还没提交事务),所以该版本的记录对当前事务不可见。
- 如果记录的 trx_id 不在 m_ids列表中,表示生成该版本记录的活跃事务已经被提交,所以该版本的记录对当前事务可见。
??总结
- 版本未提交,不可见;
- 版本已提交,但是是在视图创建后提交的,不可见;
- 版本已提交,而且是在视图创建前提交的,可见。
??update特例
在这个例子中,如果还按上边的访问规则来看的话,应该是读取不到102这个版本来着,但实际情况是如何呢?
如果读取不到的话:那事务B还是在原来的k基础上去+1,那么事务C的更新相当于是丢失了!
这里就涉及到了我们开篇讲到的当前读,更新数据都是先读后写的,这个读,就是“当前读”。
而且当前读需要对数据行加锁,此处由于事务C已经提交了,释放了锁【两阶段协议】,因此事务B可以直接查到,若事务C还未提交的话,还需要阻塞等待。
?♂️?♂️45讲疑问
可能看了45讲的小伙伴会有疑问,45讲里边这个图
这样,对于当前事务的启动瞬间来说,一个数据版本的 row trx_id,有以下几种可能:
- 如果落在绿色部分,表示这个版本是已提交的事务或者是当前事务自己生成的,这个数据是可见的;
- 如果落在红色部分,表示这个版本是由将来启动的事务生成的,是肯定不可见的;
- 如果落在黄色部分,那就包括两种情况
a. 若 row trx_id 在数组中,表示这个版本是由还没提交的事务生成的,不可见;
b. 若 row trx_id 不在数组中,表示这个版本是已经提交了的事务生成的,可见。
这个图很容易迷惑到我们,让我们误以为黄色部分跟未提交事务集合是等同的,那怎么落在黄色部分里边,还能再细分成两种情况呢?
melo画了个花里胡哨的图,来看看计算的过程【如有错误之处还请指正】
- 1-10就是45讲里边的绿色部分,11-15是黄色部分,15之后是红色部分
- 如此可以看到,黄色部分里边,还是有一些不在m_ids里边的吧,不要被表面的图像迷惑了
- 并不是说只有11之前的,才是已提交事务,11-15里边也是可能会有已提交事务的
注意,并不是开启事务就生成了,得执行快照读了才会
RC: 在事务中每一次执行快照读都会生成
RR:仅在事务中第一次执行快照时生成,后续都是复用这个readview
但是如果事务中进行了当前读的操作,比如事务中进行了update操作,后续再查询就会重新生成ReadView
其实就是上边的update特例
?undo log
当读取记录时,若该记录被其他事务占用或当前版本对该事务不可见,则可以通过 undo log 读取之前的版本数据,以此实现快照读
在 InnoDB 存储引擎中 undo log 分为两种: insert undo log 和 update undo log:
- insert undo log :指在 insert 操作中产生的 undo log。因为 insert 操作的记录只对事务本身可见【只在事务回滚时需要】,对其他事务不可见,故该 undo log 可以在事务提交后直接删除。不需要进行 purge 操作
- update undo log :update 或 delete 操作中产生的 undo log。该 undo log可能需要提供 MVCC 机制,因此不能在事务提交时就进行删除。提交时放入 undo log 链表【下文的版本链】,等待 purge线程 进行最后的删除
?版本链
类似一个链表,通过回滚指针,串联起来
- 链表头部是最新的数据,尾部是最旧的记录
??RC的例子
先看事务5里边,两次快照读生成的readview是怎样的?
- 第一次执行,此时活跃的事务id有【3,4,5】(2已经提交了)
- 最小即是3,最大【注意是预分配最大】是6
- 创建该事务的id自然是5
第二次快照读也是同样的分析方式
?判断能查到哪个事务记录
我们想知道第一次快照读,读取到的是哪个事务对应的记录【左下角中四个记录】
比如拿 0x0003这条记录来分析,trx_id是3,去跟第一个readview比对
- 判断是否是当前事务创建的记录,3!=5,说明不是
- 判断是否已经提交了【小于min_trx_id】,3不小于3,则还未提交
- 判断是否是创建readview之后才创建的事务记录【大于max_trx_id】,3不大于,则不是
- 判断数据是否已经提交【不在m_ids】里边,3在说明还未提交
因此,第一次快照读,是没法读取到 0x0003这条记录的
RR的例子
具体如何分析,跟上边RC是一样的,这里就不再赘述
只需要注意:如果期间出现了当前读,则会重新生成readview
MVCC就是为快照读而生的,维护不同的快照版本,使得不同事务的读-写操作不会冲突,实现多版本并发控制,借助MVCC,数据库可以实现READ COMMITTED,REPEATABLE READ等隔离级别
?下篇预告
这篇我们主要讲的是MVCC多版本并发控制,结合了事务的隔离级别,而关于事务背后的原理,相关的日志,这些我们留到后边再来详解。
?参考文献
- MySQL45讲
- 黑马MySQL视频
收藏=白嫖,点赞+关注才是真爱!!!本篇文章如有不对之处,还请在评论区指出,欢迎添加我的微信一起交流:Melo__Jun
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK