一致性视图-readview机制
一致性视图-readview机制
在我们开始一个事务并执行第一条查询语句,mysql会为我们创建的这个事务生成一个一致性视图-readview,这个视图由未提交事务的id数组(其中最小的事务id记为min_id),和最大的已创建的事务id组成。 例如 有三个事务 t1(id=1) t2(id=2) t3(id=3),其中id=1 和 id=2的事务未提交事务,t3是刚刚创建的事务,因此readview组成为 [1,2] 3 。
并且将事务划分为三个区域,其中
事务id < min_id 的为 已提交事务,查询的数据是可见的
事务id > max_id 的为 未开始事务,查询出来的数据是不可见
min_id <= 事务id <= max_id 的为 已经提交事务和未提交事务,查询出来数据分成两种情况
- 在readview 未提交事务的id数组中的 事务 查询出来的数据 是不可见的(如果当前事务处在 未提交事务的id数组中 则是可见的,下述例子则是这种情况)
- 在readview 中不处在未提交事务id数组中的 事务 查询出来的数据 是可见的
先记住有这么一个机制,我们后面会讲这个机制是怎么保证可重复读的。
undo日志链
还记得每条数据都有几个隐藏列吗?其中一个列记录着 事务ID ,也就是说哪一条事务操纵到了该条数据,则在该条数据的事务ID列会记录着操作该数据的事务ID。如果有两个及两个以上的事务,操作同一条数据,那么是将事务ID直接覆盖成后面的事务ID吗?显然不是的,MySQL通过生成一条undo日志链来记录每个事务对数据的操作。
undo日志链 与 一致性视图-readview机制的作用
例如,有这么一个表
现在我们有两个事务
假设事务A的id为100,事务B的id为200,还有一个查询事务id为300,和一个已经提交的事务id=20
事务A 和 事务B 执行如下
我们开启了两个事务,并且两个事务都进行了查询语句
现在在事务A中对id为 1 的 数据 进行修改
然后我们对事务A进行提交,再到事务B中查询,发现数据仍然是没有发生变化的。
难道是事务B默认备份了我们要查询的数据?这种效率太低了,不可能
现在我们正式讲一下在上述情况下 undo日志链的作用
undo日志链如下图:
因为事务A对数据进行了修改,所以会在undo日志链的生成一个修改后的数据,并且该条数据还有一个回滚指针,指向未修改之前的数据(之前数据是id=20的事务已经提交的数据)。
我们在讲readview机制时候讲到,当一个事务执行查询语句时候会生成readview。而在我们事务B中,一开始就执行了查询语句,因此生成了readview,此时在事务B中的readview是这样的:
[100,200] 300;当事务A修改数据并且提交时候,我们再次查询表数据,发现表数据没有发生改变。我们来看一下此时mySQL在事务B中是怎么用undo日志链来实现可重复读的(在这之前大家可以再看一眼之前讲述readview的规则)
- 首先mysql会从undo日志链的头节点开始查询,并且查看 trx_id 字段,发现事务 id = 100
- 得到事务id=100后,根据readview的规则,判断事务id=100是否在readview的未提交事务数组中,如果在则该数据不可见就不返回,若不在则说明该条数据是已经提交过的数据是可见的。那很明显,id = 100 的事务id在readview的未提交事务数组中( [100,200] 300),所以该条数据是不可见的不返回。通过回滚指针来到下一个节点
- 再次得到该节点的事务id,此时事务id = 20 , 20 < 100 也就是说 20 < min_id,是已经提交的数据,根据readview的规则,该节点的数据是可见的,所以将该条数据返回,所以我们在事务B中看到的数据仍然 是 lihua , 而不是修改后的 lihua 250
总结
因此mySQL通过 readview机制 和 undo日志链来实现MVCC机制,实现了事务的可重复读
转载自:https://juejin.cn/post/7148254425537052680