1.首先了解下什么是read view

这里说的 read view 是InnoDB 在实现 MVCC 时用到的一致性读视图,即 consistent read view,用于支持 RC(Read Committed,读提交)和 RR(Repeatable Read,可重复读)隔离级别的实现。 
read view 并没有物理结构,作用是事务执行期间用来定义"我能看到什么数据"。

2.事务id

InnoDB 里面每个事务有一个唯一的事务 ID,叫做 transaction id。它是在事务开始的时候向 InnoDB 的事务系统申请的,是按申请顺序严格递增的。 
在innodb存储引擎下,聚簇索引记录中都包含两个必要的隐藏列: 
trx_id:每次对某条记录进行改动时,对会把对应的事务id赋值给trx_id隐藏列; 
roll_pointer:每次对某条记录进行改动时,这个隐藏列会存一个指针,可以通过这个指针找到该记录修改前的信息,也就是undo回滚段中的内容。

3.RR(Repeatable Read,可重复读)隔离级别下的实现

在可重复读隔离级别下,事务在启动的时候就"拍了个快照"。注意,这个快照是基于整库的。 
以一个事务启动的时刻为准,如果一个数据版本是在这个事务启动之前生成的,就可以看到;如果是在这个事务启动以后才生成的,就看不到,就必须要找到它的上一个版本。 
实现上,事务启动的瞬间,InnoDB 为这个事务构造了一个数组,用来保存这个事务启动瞬间,当前正在"活跃"的所有事务 ID。“活跃”指的就是,启动了但还没提交。数组里面事务 ID 的最小值记为低水位,当前系统里面已经创建过的事务 ID 的最大值加 1 记为高水位。这个视图数组和高水位,就组成了当前事务的一致性视图(read-view),不同时刻启动的事务会有不同的 read-view。

而每次事务更新数据的时候,都会生成一个新的数据版本,并且把 transaction id 赋值给这个数据版本的事务ID,记为 row trx_id。同时,旧的数据版本要保留,并且在新的数据版本中,能够有信息可以直接拿到它。 
这个read view 视图数组把所有的 row trx_id 分成了几种不同的情况:已提交事务、未提交事务集合、未开始事务。当前事务一定在低水位和高水位之间。

那么总结下来可以有几个概念就是:

m_ids:表示在生成readview时,当前系统中活跃的读写事务id数组; 
min_trx_id:表示在生成readview时,当前系统中活跃的读写事务中最小的事务id,也就是m_ids中最小的值,就是低水位; 
max_trx_id:表示生成readview时,系统中应该分配给下一个事务的id值,就是高水位; 
creator_trx_id:表示生成该readview的事务的事务id;

而数据版本的可见性规则,就是基于数据的 row trx_id 和这个一致性视图的对比结果得到的: 
1.如果被访问版本的row trx_id,与readview中的creator_trx_id值相同,表明当前事务在访问自己修改过的记录,该版本可以被当前事务访问; 
2.如果被访问版本的row trx_id,小于readview中的min_trx_id值,表明生成该版本的事务在当前事务生成readview前已经提交,该版本可以被当前事务访问; 
3.如果被访问版本的row trx_id,大于或等于readview中的max_trx_id值,表明生成该版本的事务在当前事务生成readview后才开启,该版本不可以被当前事务访问; 
4.如果被访问版本的row trx_id,值在readview的min_trx_id和max_trx_id之间,就需要判断trx_id属性值是不是在m_ids列表中, 
如果在:说明创建readview时生成该版本的事务还是活跃的,该版本不可以被访问; 
如果不在:说明创建readview时生成该版本的事务已经被提交,该版本可以被访问。

现有示例如下:

那么 事务1、事务2、事务4的查询结果是多少呢? 
事务1的两次查询结果都是 100 
事务2第一次查询是100,第二次查询是80 
事务4的第一次查询是90

这个结果是不是同你得出的结果一致呢?现在我们来解析例子中的结果: 
start transaction with consistent snapshot; 这条语句执行完就是开启了一个事务,按照可重复读的定义,一个事务启动的时候,能够看到所有已经提交的事务结果。但是之后,这个事务执行期间,其他事务的更新对它不可见。

假设在事务1之前有一个活跃事务,这个事务id是99;事务1、事务2、事务3、事务4的事务id分别是100,101,102,103;这个例子的期间没有其他事务;假设这行数据在这三个事务开始之前是row trx_id是90; 
那么: 
事务1启动的时候,read view数组中的值是[99,100] 
事务2启动的时候,read view数组中的值是[99,100,101] 
事务3启动的时候,read view数组中的值是[99,100,101,102] 
事务4启动的时候,事务3已经提交了,所以read view数组中的值是[99,100,101,103]

数据 id = 1 这条记录的数据版本如下:

从图中可以看出,第一次有效更新是事务2把(id,score) 从 (1,100)更新成了(1,90),那么此时这个数据的最新版本就变成了row trx_id 102,而trx_id 90 这个版本就成了历史版本;第二次有效更新是事务2把(id,score)从当前值(1,90)更新成了(1,80),这个数据的最新版本变成了 row trx_id 101,而trx_id 102成为了历史版本。

对于事务1来说,101就是高水位,活跃事务有[99,100],在第一次查询的时候,id=1的数据的row trx_id是90,小于read view中的最小值即低水位值99,所以此时的数据是可见的;第二次查询的时候,id=1的数据的row trx_id是101,等于read view中的高水位,数据不可见,继续往前找数据的历史版本,发现历史版本1的row trx_id是102,大于read view中的高水位,数据依然不可见,再往前找历史版本2的row trx_id是90,小于read view中的最小值即低水位值99,所以此时的数据是可见的;所以事务1两次的查询id=1的值都是100

对于事务2来说,102是高水位,活跃事务有[99,100,101],在第一次查询的时候,id=1的数据的row trx_id是102,等于read view中的高水位,数据不可见,继续往前找数据的历史版本2 row trx_id是90,小于read view中的最小值即低水位值99,所以此时的数据是可见的,第一次查询id=1结果是score=100;第二次查询的时候,id=1的数据的row trx_id是101,等于自己当前的事务id,就是说现在查询的数据的更改是自己更改的,所以查询id=1结果是score=80

事务3对数据进行了修改就提交了,对数据的修改是当前读。

对于事务4来说,104是高水位,活跃事务有[99,100,101,103],查询的时候,id=1的数据的row trx_id是101,101介于低水位和高水位之间(介于readview的min_trx_id和max_trx_id之间)且就在活跃的事务列表中,说明此时事务还没有提交,是不可见的,否则就是脏读,继续往前发现历史版本1的row trx_id是102,102介于低水位和高水位之间,但是不在活跃的事务列表中,说明此时事务已经提交,数据是可见的,所以查询id=1的结果是90

4.RC(Read Committed,读提交)隔离级别下的实现

在读提交隔离级别下,每一个语句执行前都会重新算出一个新的视图。 
在读提交隔离级别下,"start transaction with consistent snapshot;" 就等于 start transaction,start transaction 命令并不是一个事务的起点,在执行到它们之后的第一个操作 InnoDB 表的语句,事务才真正启动。 
所以示例在RC隔离级别下:

事务1的第一次查询结果是100,第二次查询结果是90 
事务2第一次查询是90,第二次查询是80 
事务4的第一次查询是90

MySQL read view 在RR和RC隔离级别下的异同的更多相关文章

  1. [原创]MySQL RR隔离级别下begin或start transaction开启事务后的可重复读?

    Server version:         5.6.21-log MySQL Community Server (GPL) 前提提要: 我们知道MySQL的RR(repeatable read)隔 ...

  2. InnoDB在MySQL默认隔离级别下解决幻读

    1.结论 在RR的隔离级别下,Innodb使用MVVC和next-key locks解决幻读,MVVC解决的是普通读(快照读)的幻读,next-key locks解决的是当前读情况下的幻读. 2.幻读 ...

  3. 【MySQL 读书笔记】RR(REPEATABLE-READ)事务隔离详解

    这篇我觉得有点难度,我会更慢的更详细的分析一些 case . MySQL 的默认事务隔离级别和其他几个主流数据库隔离级别不同,他的事务隔离级别是 RR(REPEATABLE-READ) 其他的主流数据 ...

  4. [高性能MYSQL 读后随笔] 关于事务的隔离级别(一)

    一.锁的种类 MySQL中锁的种类很多,有常见的表锁和行锁,也有新加入的Metadata Lock等等,表锁是对一整张表加锁,虽然可分为读锁和写锁,但毕竟是锁住整张表,会导致并发能力下降,一般是做dd ...

  5. Mysql 间隙锁原理,以及Repeatable Read隔离级别下可以防止幻读原理(百度)

    Mysql知识实在太丰富了,前几天百度的面试官问我MySql在Repeatable Read下面是否会有幻读出现,我说按照事务的特性当然会有, 但是面试官却说 Mysql 在Repeatable Re ...

  6. MySQL Transaction--RR事务隔离级别下加锁测试

    ============================================================================== 按照非索引列更新 在可重复读的事务隔离级别 ...

  7. MySQL Transaction--RC事务隔离级别下加锁测试

    ==============================================================================非索引列更新 在读提交的事务隔离级别下,在非 ...

  8. InnoDB MVCC RR隔离级别下的数据可见性总结

    一.背景 熟悉数据库隔离级别的人都知道,在RR(可重复读)隔离级别下,无论何时多次执行相同的SELECT快照读语句,得到的结果集都是完全一样的,即便两次SELECT语句执行期间,其他事务已经改变了该查 ...

  9. MySQL(24):事务的隔离级别

    1. 事务的隔离级别引入: 数据库是多线程并发访问的,所以很容易出现多个线程同时开启事务的情况,这样的就会出现脏读.重复读以及幻读的情况.在数据库操作中,为了有效保证并发读取数据的正确性,需要为事务设 ...

  10. mysql中不同事务隔离级别下数据的显示效果--转载

    事务是一组原子性的SQL查询语句,也可以被看做一个工作单元.如果数据库引擎能够成功地对数据库应用所有的查询语句,它就会执行所有查询,如果任何一条查询语句因为崩溃或其他原因而无法执行,那么所有的语句就都 ...

随机推荐

  1. SpringBoot能同时处理多少请求

    SpringBoot默认的内嵌容器是Tomcat,也就是我们的程序实际上是运行在Tomcat里的.所以与其说SpringBoot可以处理多少请求,到不如说Tomcat可以处理多少请求. 关于Tomca ...

  2. Aspose 导出Excel时 隐藏指定列

    Worksheet ws = wb.Worksheets[0]; ws.Cells.HideColumn(0); //隐藏Excel第一列

  3. 《探索Python Requests中的代理应用与实践》

    requests加代理 高匿API代理 此处使用的小象代理:1元100个,便宜,可以购买尝试加下代理 存活期1到2分钟 import time import requests from lxml im ...

  4. [oeasy]python0096_游戏娱乐行业_雅达利_米洛华_四人赛马_影视结合游戏

    游戏娱乐行业 回忆上次内容 游戏机行业从无到有 雅达利 公司 一枝独秀 并且带领 行业 发展起来 雅达利公司 优秀员工 乔布斯 在 朋友 帮助下完成了<pong> Jobs 黑了 Woz ...

  5. oeasy教您玩转vim - 77 - # 保留环境viminfo

    ​ 保留环境viminfo 回忆组合键映射的细节 上次我们定义了session :mks 还可以加载会话session :source Session.vim vim -S Session.vim 基 ...

  6. Python 阿里云OSS文件上传下载与文件删除及检索示例

    阿里云OSS文件上传下载与文件删除及检索示例 实践环境 运行环境: Python 3.5.4 CentOS Linux release 7.4.1708 (Core)/Win10 需要安装以下类库: ...

  7. XR实时云渲染:助力虚拟仿真实训教学呈现

    近年来以5G.云计算.大数据.物联网.人工智能.虚拟现实/增强现实为代表的新兴技术迅速发展加速创新,日益融入经济社会发展各领域全过程,世界主要国家和地区纷纷加快数字化转型战略布局. 虚拟仿真系统是一种 ...

  8. php 开发规范

    ===========================框架========================= · 使用laravel框架,原因:tp的框架路由和orm没有laravel好用 · 使用强 ...

  9. linux中whereis、which、find、locate的区别

    linux中whereis.which.find.locate的区别 1. find       fan路名含 find是最常见和最强大的查找命令,你可以用它找到任何你想找的文件. find的使用实例 ...

  10. 从C++看C#托管内存与非托管内存

    进程的内存 一个exe文件,在没有运行时,其磁盘存储空间格式为函数代码段+全局变量段.加载为内存后,其进程内存模式增加为函数代码段+全局变量段+函数调用栈+堆区.我们重点讨论堆区. 进程内存 函数代码 ...