Mysql MVCC多版本并发事务原理简要分析
1. 说明
Mysql InnoDB 事务的实现主要是通过MVCC多版本并发控制实现的,网上查阅资料基本都说的很详细,也就意味着第一次看真的很懵,本文主要以简要的易懂的方式说明其工作原理。
2. 分析
InnoDB 主要靠 undolog(回滚日志)和 readview(读视图)来实现的,这两个是需要搭配起来共同实现MVCC版本控制。
- undolog是用来记录表被更新的记录,可以视为一个链表,每个会话都可以追加记录。
- readview 是事务在开启时的一个视图,其实准确的说是事务开启后第一次使用表时的记录,记录了数据库正在运行的其它事务情况。
下文具体分析他俩的功能
2.1. undolog
undolog是用来记录表被更新的记录,可以视为一个链表,每个会话都可以追加记录。某个事务根据特定的规则来找到哪个位置的数据是它可以看见的(这么说可能不是很准确,毕竟本身不是链表,这里只是比喻一下)。
undolog主要由以下几部分组成
- db_tx_id 事务id,即某个事务开启时事务的id(这里顺带说一下,每个会话开启事务,事务的id都是递增的,只有在数据库重启之后事务id会重置,因此,之后开启的事务id是一定大于之前的)。
- db_row_id 每条数据都有一个隐式的id,即使没有规定主键的表,也有此id。
- db_roll_ptr 前一个事务指针,指向前一个事务的地址。
我们在修改数据时,A修改过,则A首先记录,此时A的事务id为1,undolog记录下A的tx_id=1,row_id,db_roll_ptr(此时db_roll_ptr指向的是之前提交过的,如果启动之后没有被更新,则为空)。
然后B也对其进行了修改,则undolog记录B的tx_id=2,row_id,db_roll_ptr指向A。
然后C也对其进行了修改,则undolog记录C的tx_id=3,row_id,db_roll_ptr指向B。
有了这个规则,就形成了一个链,可以通过最近的更新 往前找到旧的更新记录。
如下:
2.2. readview
readview 是事务在开启时的一个视图,其实准确的说是事务开启后第一次使用表时的记录,记录了数据库正在运行的其它事务情况。它主要由3段构成
up_limit_id:创建视图时候的已经提交的最小事务id,小于这个id的肯定是已经提交过的,是本事务中有效的;
trx_list: 创建视图时正在进行的事务,也就是打开了的事务;
low_limit_id: 创建视图时下一个将要开启的事务id,大于等于这个id的都是快照记录事务之后的数据了,与本次事务肯定没有关系
这里可能有一个不好理解的,就是low_limit_id为啥不是trx_list中最大的事务id+1。别着急,后面我们有一个场景会说明
2.3. 使用
undolog和readview的配合使用,需站在某一事务的角度来看全局事务和此条数据的undolog
我们分析事务此刻的readview视图,根据undolog数据最近到最早的顺序看数据的可见性,
- 判断是否是事务自己,如果是自己修改的数据肯定可见,匹配成功,可见
- 再判断是否比最小的事务小,小于则说明是已经提交过的事务,可见
- 判断是否是比下一个要开启的事务大,大于或等于则匹配成功,不可见
- 再判断是否是在trx_list正在开启的事务中,如果是,则则匹配成功,不可见,因为事务隔离;
- 其余为可见
有这样一个场景:
事务1,2,3,4依次打开,我们站在事务2的角度来看此问题。然后事务4执行对应表数据的修改,并完成提交,这时候事务4就已经完成了。
然后执行事务2的对应表数据行的查询语句,创建readview,此时事务2readview状态应该如下:
up_limit_id = 1,trx_list =1,3 , low_limit_id=5,(因为此刻最大的事务id是4,下一个就应该是5),而4是已经完成的,姑且放在trx_list和low_limit_id中间:
再然后系统开启了事务5,6,7,其中事务5不处理,事务6,7对这条数据执行了修改(本例以快照读说明,因为当前读事务2会获取锁,导致后面的事务阻塞),系统的事务则变为:
此时undolog,应该如下:
现在我们来分析事务2在undolog中看到的数据情况
首先找到的是最近的数据事务7更新的,根据上面规则来看
- 不是事务2自己,不符合
- 比最小的事务大,不符合
- 比下一个要开启的事务大,符合,不可见
因此事务2不可见事务7修改的数据
因此需要查看事务7undolog记录的回滚指针对应的数据,即事务6修改的
同理可以知道事务6和7一样,事务2也不可见
再从事务6修改的数据的回滚指针中看到,下一条数据是事务4修改的。
- 事务4不是事务2自己,不符合
- 比最小的大,不符合
- 比5小,不符合
- 不在trx_list中
因此4修改的数据是可见的
其实简单一点的我们可以得出,在本事务开启时,记录已有的其它事务作为判断依据,当本事务读取快照(读取数据)时看事务开启时记录的事务中提交过的事务数据是可见的,还未提交的事务不可见,自己修改的可见。直到本事务提交都不再变化。