引入问题
首先看下面这张图,假如说一条数据经过了事务 2、3、4,到事务 5 的时候,进行两次查询,那这两次查询分别查询的是哪个事务版本的记录呢?
这就是我们要解决的问题,那么MVCC机制也就是为了解决这个问题。
具体实现
MVCC 主要依赖三个内容,分别为:隐藏字段
、undo log 版本链
、readView
隐藏字段
其实在我们存储数据库中一条数据的时候,并不单单是存储我们看到的那些数据,还会存储一些隐藏字段:最近修改的事务ID、回滚指针roll_pointer等。真实存储如下图所示:
undo log 版本链
介绍undo log之前,可以先了解一下undo log。
undo log就是回滚日志,我们之前讲到过,在插入、更新、删除的时候会产生便于数据回滚的日志。但是当插入的时候,产生的日志只要在事务提交之后就会立即删除,而更新和删除的时候,产生的日志并不会立即删除的。不仅回滚时需要,MVCC 版本访问的时候也是需要的。
undo log版本链就是把每次修改的undo log以链表形式记录下来,生成的一条版本链表。
readView
readView 是快照读 SQL 执行时 MVCC 提取数据的依据,记录并维护当前活跃的(未提交的)事务 ID。readView 中有四个字段:
第一个字段:假如说事务 5 第一次查询,那么当前活跃的事务 ID 就是 3,4,5
第二个字段:最小活跃事务 ID 那就是 3
第三个字段:最大事务是 5,所以预分配事务 id 就是 6
第四个字段:创建者事务 id 就是 5
有了上面那些字段之后呢,为了获取准确的数据,readView 定义了一些数据返回的规则,规则如下:
有了这些规则之后,我们就可以根据规则来选择合适的版本数据。除此之外,我们需要知道不同隔离级别下,生成的读视图(readView )时机是不同的。
例如 RC 在事务中每一次执行快照读生成readView;RR 仅在事务第一次执行快照读生成readView,后续复用上面的readView
下面简单举例,RC 级别下生成的 readView:
再结合规则我们来进行判断看选择哪一次 undo log 链中的事务来进行读:
比如第一个 4 来进行判断,① 条件不满足,② 条件不满足,③ 条件不满足,④ 条件也不满足,所以说事务 4 是不选择的
接着 3,①,② ,③ ,④ 条件也都不满足,所以事务 3 不选择
看事务 2,① 条件不满足,但是② 就满足了,也满足 ④,所以读的就是事务 2 中的数据
因为是 RC 级别下,所以第二次查询又生成了一个 readView,根据上面规则,很容易就能选择到事务 3 满足条件
RR 级别下生成 readView:
总结
MVCC 机制主要是包括三大部分:隐藏字段
、undo log 版本链
、readView
我们存储的数据会默认也存储两个隐藏字段,分别为当前修改的事务 id 和 roll_pointer 回滚指针
undo log 是回滚日志,存储老版本的数据。多个事务操作某一行数据,记录不同事务修改数据的版本,通过回滚指针形成的链表叫做 undo log 版本链
readView 解决了事务查询选择版本的问题,根据 readView 的匹配规则和当前事务 id 判断该访问哪个版本的数据。不同隔离级别 readView 是不一样的,最终访问结果也是不同的。比如 RC,每一次执行快照读生成 readView,RR 仅在事务第一次执行快照读时生成 readView,后续复用
参考链接
https://www.bilibili.com/video/BV1yT411H7YK?p=32&vd_source=8e7bfe48e222037c101ebbfa23645009