给你讲懂 MVCC
很久以前,有位面试官问到,你知道 mysql 的事务隔离级别吗,“额 O__O …,不太清楚”,完了之后我就去网上找相关的文章,找到了这篇MySQL 四种事务隔离级的说明, 文章写得特别好,看了这个就懂了各个事务隔离级别都是啥,不过看了这个之后多思考一下的话还是会发现问题,这么神奇的事务隔离级别是怎么实现的呢
其中 innodb 的事务隔离用到了标题里说到的 mvcc,Multiversion concurrency control, 直译过来就是多版本并发控制,先不讲这个究竟是个啥,考虑下如果纯猜测,这个事务隔离级别应该会是怎么样实现呢,愚钝的我想了下,可以在事务开始的时候拷贝一个表,这个可以支持 RR 级别,RC 级别就不支持了,而且要是个非常大的表,想想就不可行
腆着脸说虽然这个不可行,但是思路是对的,具体实行起来需要做一系列(肥肠多)的改动,首先根据我的理解,其实这个拷贝一个表是变成拷贝一条记录,但是如果有多个事务,那就得拷贝多次,这个问题其实可以借助版本管理系统来解释,在用版本管理系统,git 之类的之前,很原始的可能是开发完一个功能后,就打个压缩包用时间等信息命名,然后如果后面要找回这个就直接用这个压缩包的就行了,后来有了 svn,git 中心式和分布式的版本管理系统,它的一个特点是粒度可以控制到文件和代码行级别,对应的我们的 mysql 事务是不是也可以从一开始预想的表级别细化到行的级别,可能之前很多人都了解过,数据库的一行记录除了我们用户自定义的字段,还有一些额外的字段,去源码data0type.h里捞一下
一个是 DATA_ROW_ID
,这个是在数据没指定主键的时候会生成一个隐藏的,如果用户有指定主键就是主键了
一个是 DATA_TRX_ID
,这个表示这条记录的事务 ID
还有一个是 DATA_ROLL_PTR
指向回滚段的指针
指向的回滚段其实就是我们常说的 undo log,这里面的具体结构就是个链表,在 mvcc 里会使用到这个,还有就是这个 DATA_TRX_ID
,每条记录都记录了这个事务 ID,表示的是这条记录的当前值是被哪个事务修改的,下面就扯回事务了,我们知道 Read Uncommitted
, 其实用不到隔离,直接读取当前值就好了,到了 Read Committed
级别,我们要让事务读取到提交过的值,mysql 使用了一个叫 read view
的玩意,它里面有这些值是我们需要注意的,
m_low_limit_id
, 这个是 read view 创建时最大的活跃事务 id
m_up_limit_id
, 这个是 read view 创建时最小的活跃事务 id
m_ids
, 这个是 read view 创建时所有的活跃事务 id 数组
m_creator_trx_id 这个是当前记录的创建事务 id
判断事务的可见性主要的逻辑是这样,
当记录的事务
id
小于最小活跃事务 id,说明是可见的,如果记录的事务
id
等于当前事务 id,说明是自己的更改,可见如果记录的事务
id
大于最大的活跃事务id
, 不可见如果记录的事务
id
介于m_low_limit_id
和m_up_limit_id
之间,则要判断它是否在m_ids
中,如果在,不可见,如果不在,表示已提交,可见具体的代码捞一下看看
剩下来一点是啥呢,就是 Read Committed
和 Repeated Read
也不一样,那前面说的 read view
都能支持吗,又是怎么支持呢,假如这个 read view
是在事务一开始就创建,那好像能支持的只是 RR 事务隔离级别,其实呢,这是通过创建 read view
的时机,对于 RR 级别,就是在事务的第一个 select
语句是创建,对于 RC 级别,是在每个 select
语句执行前都是创建一次,那样就可以保证能读到所有已提交的数据
本文使用署名 4.0 国际 (CC BY 4.0)许可协议,欢迎转载、或重新修改使用,但需要注明来源。
本文作者: Nicksxs
创建时间: 2020-04-26
本文链接: 给你讲懂 MVCC
版权声明: 本文为 InfoQ 作者【Nick】的原创文章。
原文链接:【http://xie.infoq.cn/article/eea02fd3543e13cdcb29ac672】。文章转载请联系作者。
评论