承接上文MySQL的MVCC底层原理
上文说到时序图中的第12步
这篇文章咱接着聊完
时序图
![图1](https://gitee.com/pingfanrenbiji/pictures/raw/master/2021-11-27/1638023786567-image.png)
时序图第12步中的查询事务的read-view情况
![图2](https://gitee.com/pingfanrenbiji/pictures/raw/master/2021-11-27/1638020881365-image.png)
read-view为[100,200],300
即未提交数组事务id为100和200
最大的事务id为300
![图3](https://gitee.com/pingfanrenbiji/pictures/raw/master/2021-11-27/1638020688641-image.png)
去id为1的数据的版本链中根据比对规则去查找数据
最新的一条数据
![图4](https://gitee.com/pingfanrenbiji/pictures/raw/master/2021-11-27/1638020979167-image.png)
事务id为100
100在这个查询的未提交的事务数组中
所以这条数据对于当前查询事务是不可见的
那么继续在版本链上找
根据回滚指针找到第二条数据
![图5](https://gitee.com/pingfanrenbiji/pictures/raw/master/2021-11-27/1638021094990-image.png)
和最新的那条数据也是一样的
这条数据对于当前事务是不可见的
那么继续根据回滚指针找数据
![图6](https://gitee.com/pingfanrenbiji/pictures/raw/master/2021-11-27/1638021171193-image.png)
这条数据的事务id是300
![图7](https://gitee.com/pingfanrenbiji/pictures/raw/master/2021-11-27/1638021299341-image.png)
事务id为300也是落到黄色部分的区间
根据版本链的对比规则可知
这一行数据的事务id300不在未提交事务的数组中
说明这个版本的数据是已经提交的事务产生的
这是可见的
截止目前找到了这条数据
就会把这条数据返回
继续看下时序图第14和15步骤
又更新了2个SQL语句
第14步:
update account set name='人' where id =1;
第15步:
update account set name='笔' where id =1;
那么此时id=1的数据版本链是
![图8](https://gitee.com/pingfanrenbiji/pictures/raw/master/2021-11-27/1638023081148-image.png)
时序图第16步
select name from account where id =1
![图9](https://gitee.com/pingfanrenbiji/pictures/raw/master/2021-11-27/1638023223225-image.png)
可重复读的readview会沿用第一次查询时候的readview[100,200],300
如果已提交读的话每次select都会生成一次最新的readview 未提交事务数组为[200]
可重复读readview去最新版本链中找
![图10](https://gitee.com/pingfanrenbiji/pictures/raw/master/2021-11-27/1638023443930-image.png)
先找第一条数据
![图11](https://gitee.com/pingfanrenbiji/pictures/raw/master/2021-11-27/1638023496185-image.png)
这条数据的事务id为200
200在未提交的事务列表中
那么这条数据所对应的事务对于查询事务来说还未提交
所以不可见
根据上面说的查询规则
最终还是找到了
![图12](https://gitee.com/pingfanrenbiji/pictures/raw/master/2021-11-27/1638023603387-image.png)
所以第16步即第三次查询查询的结果为上面这条数据
截止目前已经将时序图上的所有环节都描述清楚了
接下来修改下时序图
![图13](https://gitee.com/pingfanrenbiji/pictures/raw/master/2021-11-27/1638024151047-MVCC.png)
将查询事务1去掉
换成了查询事务2
那么时序图中第14步会查询出来什么数据
select name from account where id =1
此时的readview和数据版本链是
![图14](https://gitee.com/pingfanrenbiji/pictures/raw/master/2021-11-27/1638024303420-image.png)
第一条数据
![图15](https://gitee.com/pingfanrenbiji/pictures/raw/master/2021-11-27/1638024356918-image.png)
这条数据的事务id为200
![图16](https://gitee.com/pingfanrenbiji/pictures/raw/master/2021-11-27/1638024397287-image.png)
落在黄色区间
那么这条数据对应的事务id=200对于查询事务2
还未提交
所以不可见
第二条数据也是一样的
第三条数据
![图17](https://gitee.com/pingfanrenbiji/pictures/raw/master/2021-11-27/1638024509311-image.png)
事务id=100是小于min_id=200的
所以落在绿色区间(已提交事务区间)
这个数据对于查询事务2是可见的
所以就会把这条数据返回
再次总结下数据版本链的比对规则
得到read-veiw的过程
![图18](https://gitee.com/pingfanrenbiji/pictures/raw/master/2021-11-27/1638025652329-image.png)
版本链对比过程
![图19](https://gitee.com/pingfanrenbiji/pictures/raw/master/2021-11-27/1638025740208-image.png)
图14中的版本链数据
其实就是快照数据
记录了历史每个事务对于这条数据的变更记录
并非每个事务都复制了一份这条数据
快照数据是针对于全库全表的
举例说明
调整下时序图
![](https://gitee.com/pingfanrenbiji/pictures/raw/master/2021-11-27/1638026851921-MVCC.png)
在查询事务2上添加了步骤8
select * from t;
查询t表
此时就会生成一个readview
那么第15步就会使用这个readview去版本链中比对查询数据
![](https://gitee.com/pingfanrenbiji/pictures/raw/master/2021-11-27/1638027216228-image.png)
所以任何一个查询事务(session级别的)sql语句生成的readview
是针对全库所有表的快照
在当前事务 查询所有表都会沿用这个readview
删除数据
对于删除的情况可以认为是update的特殊情况
会将版本链上最新的数据复制一份
![](https://gitee.com/pingfanrenbiji/pictures/raw/master/2021-11-27/1638027648566-image.png)
这是最原始的一条数据
如果将之删除
首先会复制这一条数据
比如删除的事务id是300
![](https://gitee.com/pingfanrenbiji/pictures/raw/master/2021-11-27/1638027857495-image.png)
接下来会在复制的这条数据的信息头的标记位写上ture
![](https://gitee.com/pingfanrenbiji/pictures/raw/master/2021-11-27/1638028022781-image.png)
表明这条记录虽然在版本链中但已经被删除了
再来查询的话 按照刚才说的readview和版本链的比对规则找到了是这条记录
会判断下这条记录头信息中的标记位是否为ture
如果为ture的话 则不会返回
如果这条记录没有任何标记 那么就是符合的
就会返回这条数据
![](https://gitee.com/pingfanrenbiji/pictures/raw/master/2021-11-27/1638028405731-image.png)