mysql中,可重复读是怎么实现的?

2021-03-01 19:55发布

4条回答

通过一致性视图和多版本并发控制(MVCC)实现。

MVCC就是一种乐观锁的实现方式,而且是一种很常用的乐观锁实现方式。

所谓多版本,就是一行记录在数据库中存储了多个版本,每个版本以事务ID作为版本号。InnoDB 里面每个事务有一个唯一的事务 ID,是在事务开始的时候向InnoDB的事务系统申请的,并且按照申请顺序严格递增的。假如一行记录被多个事务更新,那么,就会产生多个版本的记录。

以某一行数据作为例子:


经过两次事务的操作,value从22变成了19,同时,保留了三个事务id,15、25、30。

在每个记录多版本的基础上,需要利用“一致性视图”,来做版本的可见性判断。

这里,我们要区分MySQL里面的两个”视图”概念:

  • 一个是view,通过语法create view … 实现,主要创建一个虚拟表,用来执行查询语句。

  • 一个是InnoDB用来实现mvcc的一致性视图(consistent read view),纯逻辑概念,没有物理结构,定义了在事务期间,你能看到哪些版本的数据。

我们全文提到的“视图”都是第二种,主要是支持InnoDB在“读已提交”和“可重复读”级别的并发访问问题。

  • “读未提及”级别下,没有一致性视图

  • “读已提交”级别下,会在 每个SQL开始执行的时候 创建一致性视图

  • “可重复读”级别下,会在 每个事务开始的时候 创建一致性视图

  • “串行化”级别下,直接通过加锁避免并发问题

下面,我们简单介绍一下创建一致性视图的逻辑。

以“可重复读”级别为例。

  • 当一个事务开启的时候,会向系统申请一个新事务id

  • 此时,可能还有多个正在进行的其他事务没有提交,因此在瞬时时刻,是有多个活跃的未提交事务id

  • 将这些未提交的事务id组成一个数组,数组里面最小的事务id记录为低水位,当前系统创建过的事务id的最大值+1记录为高水位

  • 这个数组array 和 高水位,就组成了“一致性视图”。

有了一致性视图后,我们就可以判断一行数据的多版本可见性了,无论是“读已提交”还是“可重复读”级别,可见性判断规则是一样的,区别在于创建快照(一致性视图)的时间。

在当前事务中,读取其他某一行的记录,对其中的版本号的可见性判断有五种情况(建议自己跟着捋一捋,挺重要的):

  • 如果版本号小于“低水位”,说明事务已经提交,那肯定 可见;

  • 如果版本号大于“高水位”,说明这行数据的这个事务id版本是在快照后产生的,那肯定 不可见;

  • 如果版本号在事务数组array中,说明这个事务还没提交,所以 不可见;

  • 如果版本号不在事务数组array中,且低于高水位,说明这个事务已经提交,所以 可见;

  • 当然,无论什么时候,自己的事务id中的任何变化,都是可见的

可以看看下面这个例子,更容易理解。

系统创建过的事务id:1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
事务A启动,拍个快照
此时未提交的事务id有:7,8,9
一致性视图:数组array[7,8,9] + 高水位16(15+1)

对于任意一行数据的可见性判断如下:

  • 小于7的,可见

  • 大于16的,说明是快照后产生的,不可见

  • 10-15,不在数组array中,说明已经提交了,可见

  • 7,8,9在array中,说明未提交,不可见

两个重要结论:

  • InnoDB 利用了“所有数据都有多个版本”的这个特性,实现了“秒级创建快照”的能力。

  • MVCC的实现,就是根据当前事务的事务id为依据创建“一致性视图”,利用一致性视图来判断数据版本的可见性。

一周热门 更多>