对MySql的RR隔离级别下MVCC机制有些疑惑

img


上图是一个演示RR隔离级别下MVCC的例子,基于这个例子有点扩展性的问题琢磨不清,特来球教一下大家:

就是图中事务5中的查找是基于id的,如果改成依照age=3来查找,会是什么情形?

当然第一次查询是能找到id=30的这条数据的,按照理想状况,第二次查询应该也能。
但是按照目前我对该机制的理解,逐个回溯历史版本的第一步是在数据库(当前最新状态)中找到对应的数据吧,此时id=30的这条数据已经被事务4改成age=10了,那查询的第一步就找不到这条数据了吧。同时也不会直接去undo log里找对应的历史数据吧,不沿着数据版本链找等于大海捞针。
那岂不是就会出现一个很反直觉的情形?——明明是RR隔离级别下的两次快照读,中间也没穿插当前读,用的ReadView都是同一个。但读到的数据已经不一样了,是不是属于直接违反可重复读的定义了?

然后我又想到一种可能,这种情形是不是属于幻读?但是就我之前学到的理论里,幻读包括的情形有“读取时不存在但试图插入数据时却提示数据已存在而插入失败”或者“查询时存在但试图删除或修改时却提示不存在”,都在试图进行DML操作时才暴露问题,但上面例子里明明是两个单纯的查询,特征上也不符合。是我用的教程太过狭隘么?还是刚刚的推断中有所偏差(压根不是幻读)?还是有啥我没有掌握或理解偏差的基础知识?实在有些迷惑,求各位指点一二,谢谢大家。

参考GPT和自己的思路:

首先,如果将查询条件从id改为age=3,那么在MVCC机制下的查询流程是类似的。事务5会创建一个ReadView,并读取ReadView创建时的最新快照数据。如果ReadView创建后事务4修改了数据,那么事务5在遍历数据历史版本时,会发现版本号为3的数据满足条件,因此会将其返回。这个过程中并不会涉及到undo log,因为读取操作不会对数据进行修改。

然而,如果此时有其他事务对数据进行修改或插入操作,就有可能出现幻读的情况。幻读是指在一个事务读取数据时,由于其他事务插入或删除数据,导致事务执行的查询结果与之前不同。在RR隔离级别下,事务读取的数据是基于当前时间创建的快照,因此没有办法防止幻读。MySQL的InnoDB引擎使用Next-Key Locks来通过锁定索引范围来避免幻读。具体细节可以参考InnoDB的实现文档。

我的理解是这样的,开启事务后的首个查询操作,会触发快照读也就是创建readView视图,该事务后面的select操作基于该视图,如果中间有其他事务做了当前读,update/delete/add 的操作不在行锁上,也就是存在间隙和其他字段索引没锁问题,就会覆盖当前视图中的事务id导致在视图中出现幻读的感觉。