解密MySQL产生幻读的根本原因(首次公开)

文章目录

  • 前言
  • 一、undo log
  • 二、Read View
  • 三、可见性判断逻辑(解决脏读和不可重复读的关键)
  • 四、解密RR隔离级别下产生幻读的原因


前言

上一篇文章介绍了什么是脏读、不可重复复、幻读的概念和现象。这次我就来说一下Mysql是如何通过MVCC机制来解决脏读、不可重复的问题的。
还有幻读现象是怎么发生的!


一、undo log

MVCC机制的实现是基于undo log的,所以我们要先看一下undo log。

undo log是Innodb存储引擎特有的日志,也是为了MVCC机制的实现做支撑的。
在InnoDB聚簇索引记录中都包含两个隐藏列:
trx_id:每次对某条记录进行改动时,对会把对应的事务id赋值给trx_id隐藏列;
roll_pointer:每次对某条记录进行改动时,这个隐藏列会存一个指针,可以通过这个指针找到该记录修改前的信息;

解密MySQL产生幻读的根本原因(首次公开)_第1张图片

二、Read View

解密MySQL产生幻读的根本原因(首次公开)_第2张图片
ReadView 生成时机非常重要,不同时机生成的ReadView肯定不一样。
数据库的RC隔离和RR隔离就是通过在不同时机生成ReadView来实现不一样的效果的。
同样,RR隔离级别下,只会在第一执行SQL的时候,会生成一次ReadView。RC隔离级别下,每次执行SQL前都会生成一次ReadView。

三、可见性判断逻辑(解决脏读和不可重复读的关键)

各个事务直接是并行的,开启以后,每个事务执行的增、删、改都会记录到undo log日志里。
每个事务在执行查询SQL的时候,会根据隔离级别获取ReadView。然后通过ReadView去undo log日志里去查找,根据一定的判断逻辑,判断数据的可见性。
具体的判断逻辑如下图:
解密MySQL产生幻读的根本原因(首次公开)_第3张图片
通过上面ReadView的判断逻辑加上ReadView的生成时机,便可以 控制事务的隔离性,是RC级别还是RR级别。
如果是每次SQL执行之前都生成新的ReadView,那就是RC级别,事务A中两次相同条件的SQL可能会查询出不一样的结果,因为其他事务在两次执行直接有可能会提交修改的SQL的事务,而RC级别,会每次Select都会生成新的ReadView,所以就会读取到其他事务commit的数据。所以在RC级别下,是会有不可重复读的问题。
但是如果是RR级别的话,只在生成事务ID的时候,生成一次ReadView,之后的所有select都是通过这一个不变的ReadView去Undo log去查找数据的,所以,就算这时候有其他事务commit了数据,由于不满足create_tx_id相同,所以是读取不到其他事务提交的数据的。

四、解密RR隔离级别下产生幻读的原因

先展示一个幻读的问题:

先查看当前的事务隔离级别,确定是 RR 级别





通过前面的截图,我们看到了幻读的现象。在RR的级别下,由于执行了一条update语句,就读到其他事务commit的数据,出现了不可重复读的现象,这现象我们通常称之为幻读。

ReadView为什么不能杜绝幻读这种现象呢?
其实我们还是根据上面的ReadView的可见性逻辑去undo log里查找数据就能发现具体原因。
根据我们刚才的步骤来看,幻读现象的出现是因为我们执行了一条update语句。在没有执行update语句之前,我们两次的查询语句返回的都是两条数据,都没有返回其他事务已经提交的数据。
但是在我们执行了一条update语句以后,我们就能查到其他事务提交的数据了。这是为什么呢?
我根据我们之前的ReadView的可见性逻辑判断去思考,肯定是update后,undo log里面的数据变化了,由之前的不满足可见性的条件,变为满足。
在update执行之前,id=3的数据,虽然已经提交,但是tx_id是下面窗口事务的tx_id,
update 执行以后,id = 3的数据,在undo log日志里,的tx_id已经变成的当前事务的id,所以在执行select 的时候, 就满足了与 creator_tx_id 相等的条件了,所以这条数据就变成了对当前事务可见。

总结一下:
产生幻读的根本性原因,因为update、insert、delete这些语句会在记录在undo log日志里,并且creator_tx_id 标记为当前事务的id,所以之后再进行select的时候,就满足了 和creator_tx_id 条件相等的条件,就满足了可见性。

你可能感兴趣的:(MySQL,数据库,mysql,幻读,MVCC,ReadView)