最近在学习MVCC,在学习过程中,很疑惑RC(读已提交)和RR(可重复度)级别都用到了MVCC来进行不加锁的读,但是为什么RR级别可以解决幻读,对于RC级别不行?

本文主要解答上面那个疑问,关于MVCC的,还请移步两位大神的博客,进行深入学习:MySQL 8.0 MVCC 源码解析, MySQL事务与MVCC如何实现的隔离级别?

MVCC即多版本并发控制,能够保证多个读请求之间不会进行阻塞,根据事物隔离级别和事物id来确定当前事物能够查询到的数据的版本。对于每行记录来说,可能会存在多个版本,而这些版本会使用使用链表进行关联起来,从而控制一个事务能够查询到的数据的版本。

首先需要明白事务的隔离级别和MVCC有密切的关系,你能深入事务的隔离级别,那么对于理解MVCC是很有帮助的,反过来,你能理解MVCC,再去理解事务的隔离级别也会有不一样的感受。

面试题:MySQL 的可重复读怎么实现的?

InnoDB 会在每行记录后面增加三个隐藏字段:

DB_ROW_ID:行ID,随着插入新行而单调递增,如果有主键,则不会包含该列。

DB_TRX_ID:记录插入或更新该行的事务的事务ID。

DB_ROLL_PTR:回滚指针,指向 undo log 记录。每次对某条记录进行改动时,该列会存一个指针,可以通过这个指针找到该记录修改前的信息 。当某条记录被多次修改时,该行记录会存在多个版本,通过DB_ROLL_PTR 链接形成一个类似版本链的概念。

上面的是每行记录都会有隐藏列,同一记录的修改记录会被链接成一个链表,而对于每个事物来说,访问记录时,是从每行记录的头部开始算起的。对于 RC(READ COMMITTED) 和 RR(REPEATABLE READ) 隔离级别的实现就是通过上面的版本控制来完成。

思考下面一个问题:

在RC(读已提交)和RR(可重复度)级别下,MVCC都会生效,那么为什么RC不可以解决幻读,而RR可以解决幻读?

原因: 两种隔离界别下的核心处理逻辑就是判断所有版本中哪个版本是当前事务可见的处理针对这个问题InnoDB在设计上增加了ReadView的设计,ReadView中主要包含当前系统中还有哪些活跃的读写事务,把它们的事务id放到一个列表中,我们把这个列表命名为为m_ids。

以上内容是对于 RR 级别来说,而对于 RC 级别,其实整个过程几乎一样,唯一不同的是生成 ReadView 的时机,RR 级别只在事务开始时生成一次,之后一直使用该 ReadView。而 RC 级别则在每次 select 时,都会生成一个 ReadView。

简易版的说:
       当前活跃的事务列表集合,从小到大的顺序排列,在这个集合中的事务列表说明并未提交,对于本事务来说是不可见的,比该事件列表最大的id还要大,说明本事务执行时,修改记录的事务还未开始,所以比该事件列表最大的id还要大的,那么就是不可见的

在RR级别下,事务执行第一个 select 语句的时候,会生成一个当前时间点的事务快照 ReadView,主要包含以下几个属性:
trx_ids:生成 ReadView 时当前系统中活跃的事务 Id 列表,活跃的事务 Id 列表从小到大的顺序排列,就是还未执行事务提交的。
creator_trx_id:生成该 ReadView 的事务的事务 Id。

在访问某条记录时,会根据该事务中的事务快照 ReadView进行判断:

  1. 行记录中的事务id是否为该事务本身的id,相同则说明自己创建的,可见
  2. 行记录中的事务id比事务快照 ReadView中最小的还小,表明该记录为之前提交的,能够被当前事务访问。
  3. 行记录中的事务id比事务快照 ReadView中最大的事务id还要大,表明该记录为之后提交的,不能被当前事务访问
  4. 行记录中的事务id在活跃的事务 Id 列表之间,那么就使用二分,判断是否能在活跃集合中找到该事务id,能找到,说明该事务快照时,还未提交,本事务无法访问,如不在活跃事务集合中,说明已提交,可以访问

在进行判断时,首先会拿记录的最新版本来比较,如果该版本无法被当前事务看到,则通过记录的 DB_ROLL_PTR 找到上一个版本,重新进行比较,直到找到一个能被当前事务看到的版本。

下面的面试可以先不说的,先说上面的,如果追问再详细说。
接下来进入正题,以 RR 级别为例:每开启一个事务时,系统会给该事务会分配一个事务 Id,在该事务执行第一个 select 语句的时候,会生成一个当前时间点的事务快照 ReadView,主要包含以下几个属性:

trx_ids:生成 ReadView 时当前系统中活跃的事务 Id 列表,就是还未执行事务提交的。

up_limit_id: 取 trx_ids 中最小的那个,trx_id 小于该值都能看到。

low_limit_id:生成 ReadView 时系统将要分配给下一个事务的id值,trx_id 大于等于该值都不能看到。

creator_trx_id:生成该 ReadView 的事务的事务 Id。

有了这个ReadView,这样在访问某条记录时,只需要按照下边的步骤判断记录的某个版本是否可见:

  1. 如果被访问版本的trx_id与ReadView中的creator_trx_id值相同,意味着当前事务在访问它自己修改过的记录,所以该版本可以被当前事务访问。
  2. 如果被访问版本的trx_id小于ReadView中的up_limit_id值,表明生成该版本的事务在当前事务生成ReadView前已经提交,所以该版本可以被当前事务访问。
  3. 如果被访问版本的trx_id大于等于ReadView中的low_limit_id值,表明生成该版本的事务在当前事务生成ReadView后才开启,所以该版本不可以被当前事务访问。
  4. 如果被访问版本的trx_id属性值在ReadView的up_limit_id和low_limit_id之间,那就需要判断一下trx_id属性值是不是在trx_ids列表中。如果在,说明创建ReadView时生成该版本的事务还是活跃的,该版本不可以被访问;如果不在,说明创建ReadView时生成该版本的事务已经被提交,该版本可以被访问。

在进行判断时,首先会拿记录的最新版本来比较,如果该版本无法被当前事务看到,则通过记录的 DB_ROLL_PTR 找到上一个版本,重新进行比较,直到找到一个能被当前事务看到的版本。
      
      以上内容是对于 RR 级别来说,而对于 RC 级别,其实整个过程几乎一样,唯一不同的是生成 ReadView 的时机,RR 级别只在事务开始时生成一次,之后一直使用该 ReadView。而 RC 级别则在每次 select 时,都会生成一个 ReadView。

此时我们再思考一下RC和RR的区别:RC  读已提交、     RR  可重复读
      在RC模式下会存在同一个SQL查询出不同的结果,相对于数据的修改,为什么会这样,就因为在RC模式下每次都会生成一个ReadView,

使用MVCC来解释读已提交造成的不可重复读问题, 每次select都会生成一个ReadView, 以发生时间4为例,如下图

RC的情况

查看id = 1的数据,根据上面ReadView的规则判断可见,

trx_ids  为[1],此时只有事务1正在处于活跃状态
up_limit_id  = 1,  此时最小的事务编号为1
low_limit_id = 3, 下次要分配的事务id
creator_trx_id = 1,当前事务

所以可以得知,  当前数据的事务版本号在up_limit_id 、low_limit_id之间, 符合条件四,然后对trx_ids  进行二分查询后发现查找不到,此时就说明事务id = 2的已被提交, 当前数据可以被查看到。所以发生时间4时,可以看到名称已经被修改成了 李四,也就造成不可重复读的现象。
        
RR的情况
查看id = 1的数据,根据上面ReadView的规则判断可见,

trx_ids  为[1],此时只有事务1正在处于活跃状态
up_limit_id  = 1,  此时最小的事务编号为1
low_limit_id = 2, 下次要分配的事务id
creator_trx_id = 1,当前事务

所以可以得知,  当前数据的事务版本号等于low_limit_id最大的版本号, 符合条件3,所以当前行不能被访问到,该版本无法被当前事务看到,则通过记录的 DB_ROLL_PTR 找到上一个版本,通过undo log日志查找到上一个版本进行对比后,发现数据事务id和当前事务id相同,所以可以访问到名称为张三的数据。

MVCC下的RR和RC级别的区别和实现相关推荐

  1. 一图看懂MVCC机制,RC级别解决脏读问题,RR级别怎么解决不可重复读问题【MySQL系列】

    说起事务隔离级别和各自解决的问题,相信学过MySQL的人都倒背如流, 三类问题:脏读.不可重复读.幻读问题: 四种隔离级别: 读不提交,最低的隔离级别,存在脏读.不可重复读.幻读问题: 读已提交,能解 ...

  2. mysql数据库新建一个递增的_分享一个mysql实验—基于数据库事务隔离级别RR及RC的测试...

    概述 今天主要分享一个最近做的实验,主要是测试mysql数据库在RR和RC不同事务隔离级别下的表现. MySQL使用可重复读来作为默认隔离级别的主要原因是语句级的Binlog.RR能提供SQL语句的写 ...

  3. insert时调用本身字段_MySQL RC级别下并发insert锁超时问题 - 案例验证

    作者:网易数据库团队 DDB(网易杭研自研的MySQL数据库中间件产品)团队小伙伴发现了一个问题,觉得比较奇怪.于是找到我们,希望解释下.过程中除解释了问题的现象,也通过代码了解了更多的InnoDB ...

  4. mysql 中 RC、RR隔离级别的原理及区别

    今天分享 mysql 中 RC.RR隔离级别的原理及区别: 1.首先简介mysql四种隔离级别: 1)未提交读(READ UNCOMMITED)脏读 2) 已提交读 (READ COMMITED)简称 ...

  5. ubuntu下/etc/rc.local和/etc/init.d/rc.local的区别

    在ubuntu下要把一个程序加入开机启动,一般可以通过修改rc.local来完成,但ubuntu下有两个rc.local文件.分别是/etc/rc.local和/etc/init.d/rc.local ...

  6. C++在linux环境下获取毫秒、微妙级别时间

                    C++在linux环境下获取毫秒.微妙级别时间 一.时间单位简介:     1秒  = 1000毫秒     1秒  = 1000000微秒     1秒  = 100 ...

  7. [js] 解释下深度优先遍历和广度优先遍历的区别及如何实现

    [js] 解释下深度优先遍历和广度优先遍历的区别及如何实现 1.深度优先采用堆栈结构,先进后出,所占的空间较小,执行时间较长: 2.广度优先采用队列结构先进先出,所占空间较大,执行时间短,空间换时间: ...

  8. linux命令grep和find怎么用,Linux下find和grep常用命令及区别介绍

    在使用linux时,经常需要进行文件查找.其中查找的命令主要有find和grep.两个命令是有区别的. 区别: (1)find命令是根据文件的属性进行查找,如文件名,文件大小,所有者,所属组,是否为空 ...

  9. [转] alpha、beta、rc各版本区别

    转自:   alpha.beta.rc各版本区别 | 运维生存时间 很多软件在正式发布前都会发布一些预览版或者测试版,一般都叫"beta版"或者 "rc版",特 ...

最新文章

  1. 写于2018年底,有点丧
  2. real time AI based system questionaires
  3. plsqldev远程连接mysql_PLSQL Developer连接远程Oracle数据库
  4. CodeForces - 1516D Cut(思维+倍增)
  5. Machine Learning——octave矩阵操作(2)——DAY3
  6. 《 FRIDA系列文章 》
  7. html5表单修改颜色,在css中更改输入搜索表单的背景色
  8. mysql connector cpp_MySQL Connector/C++(一)
  9. 激光雷达+imu_多款激光雷达性能测估
  10. 猫途鹰(tripadvisor.cn/)美国地区的酒店、景点、餐厅数据(82万条)
  11. 中国汽车市场的合资模式终究覆灭
  12. 图像质量评价之衡量标准
  13. 解决svn小乌龟没有绿勾
  14. FIL带给IPFS的创新
  15. 添加metadata到tflite模型
  16. 经典的《Rework》
  17. 520谁才是你的真爱?
  18. SDOI2013 淘金
  19. 无线传感网络的基本结构
  20. JAVA新手上路-2-javac编译

热门文章

  1. java mfcc_MFCC特征提取过程详解
  2. MEDIATOR(中介者)模式
  3. oracle新建数据库卡85,oracle数据库(新建数据库)超小白篇
  4. 独家披露武圣关羽的真正死因——绝密
  5. 半实物仿真测试平台技术背景及总体介绍
  6. 暗黑3发布!又有多少人为之疯狂
  7. 重装系统后控制面板里面没有休眠选项怎么办
  8. TCP/IP卷一:63---TCP基础之(ARQ和重传、分组窗口和滑动窗口、流量控制和拥塞控制、设置重传超时)
  9. HTTP请求错误状态码大全(HTTP Status Code)
  10. 用matlab如何画根轨迹图,MATLAB绘制根轨迹ppt课件