InnoDB MVCC RR隔离级别下的数据可见性总结
一、背景
熟悉数据库隔离级别的人都知道,在RR(可重复读)隔离级别下,无论何时多次执行相同的SELECT快照读语句,得到的结果集都是完全一样的,即便两次SELECT语句执行期间,其他事务已经改变了该查询结果并已经提交。
对于这一机制的实现原理,网上常见的一种解释如下:
####MVCC在MySQL的InnoDB中的实现
在InnoDB中,会在每行数据后添加两个额外的隐藏的值来实现MVCC,这两个值一个记录这行数据何时被创建,另外一个记录这行数据何时过期(或者被删除)。 在实际操作中,存储的并不是时间,而是事务的版本号,每开启一个新事务,事务的版本号就会递增。 在可重读Repeatable reads事务隔离级别下: SELECT时,读取创建版本号<=当前事务版本号,删除版本号为空或>当前事务版本号。
INSERT时,保存当前事务版本号为行的创建版本号
DELETE时,保存当前事务版本号为行的删除版本号
UPDATE时,插入一条新纪录,保存当前事务版本号为行创建版本号,同时保存当前事务版本号到原来删除的行
上述解释确实可以让读者简单快速地理解MVCC机制的核心思想,我最开始也以为自己已经完全理解MVCC机制的实现原理了,但是当我试图利用上述原理去解释某些特别的实测结果时,却发现总是难以自圆其说。
二、无法自圆其说的用例
2.1 用例1:为什么看不到尚未提交的记录
如上图所示,事务2中,虽然记录A的创建版本号1小于当前事务版本号2,但是依然无法读取到记录A。
2.2 用例2:为什么看不到先启动事务中插入的记录
如上图所示,事务2中,虽然记录A的创建版本号小于当前事务版本号2,且记录A已经提交,但是第二次查询时,事务2依然无法查询到任何记录。
2.3 用例3:为什么可以看到后启动事务中插入的记录
如上图所示,事务1中,虽然记录A的创建版本号大于当前事务版本号1,但是事务1依然可以查询到记录A。
2.4 结论
可见,常见的对MVCC版本的实现原理的理解似乎遗漏了某些关键逻辑,导致无法解释很多特殊情况。
下面我们来一起看一下InnoDB中,MVCC机制到底是如何控制记录的可见性的。
三、遗漏的关键控制逻辑
InnoDB RR隔离界别下,MVCC对记录可见性控制,还有如下关键判定逻辑:
1. 事务ID并非在事务begin时就分配,而是在事务首次执行非快照读操作(SELECT ... FOR UPDATE/IN SHARE MODE、UPDATE、DELETE)时分配。
注:
如果事务中只有快照读,InnoDB对只有快照读事务有特殊优化,这类事务不会拥有事务ID,因为它们不会在系统中留下任何修改(甚至连锁都不会建),所以也没有留下事务ID的机会。
虽然使用SELECT TRX_ID FROM INFORMATION_SCHEMA.INNODB_TRX WHERE TRX_MYSQL_THREAD_ID = CONNECTION_ID();
查询此类事务ID时,会输出一个很大的事务ID(比如328855902652352),不过这只是MySQL在输出时临时随机分配的一个用于显示的ID而已。
2. 每个事务首次执行快照读操作时,会创建一个read_view对象(可以理解为在当前事务中,为数据表建立了一个逻辑快照,read_view对象就是用来控制此逻辑快照的可见范围的)。事务提交后,其创建的read_view对象将被销毁。
read_view对象中有三个关键字段用于判断记录的可见范围。它们分别是trx_ids、low_limit_id、up_limit_id。
. read_view->trx_ids:创建该read_view时,记录正活跃的其他事务的ID集合。事务ID在集合中降序排列,便于二分查找。
. read_view->low_limit_id:当前活跃事务中的最大事务ID+1(即系统中最近一个尚未分配出去的事务号)。
. read_view->up_limit_id:当前活跃事务中的最小事务ID。
3. 如果记录的版本号比自己事务的read_view->up_limit_id小,则该记录的当前版本一定可见。因为这些版本的内容形成于快照创建之前,且它们的事务也肯定已经commit了。或者如果记录的版本号等于自己事务的事务ID,则该记录的当前版本也一定可见,因为该记录版本就是本事务产生的。
4. 如果记录的版本号与自己事务的read_view->low_limit_id一样或比它更大,则该版本的记录的当前版本一定不可见。因为这些版本的内容形成于快照创建之后。
不可见有如下两层含义:
. 如果该记录是新增或修改后形成的新版本记录,则对新增和修改行为不可见,即看不到最新的内容;
. 如果该记录是标记为已删除形成的新版本记录,则对该删除行为不可见,即可以看到删除前的内容。
5. 当无法通过4和5快速判断出记录的可见性时,则查找该记录的版本号是否在自己事务的read_view->trx_ids列表中,如果在则该记录的当前版本不可见,否则该记录的当前版本可见。
6. 当一条记录判断出其当前版本不可见时,通过记录的DB_ROLL_PTR(undo段指针),尝试去当前记录的undo段中提取记录的上一个版本进行4~6中同样的可见性判断,如果可以则该记录的上一个版本可见。
四、用例的正确解释
为了更好的检验上面新增的知识,对部分用例进行了适当的扩展。
4.1 用例1的正确解释
4.2 用例2的正确解释
4.3 用例3的正确解释
五、总结
要正确理解InnoDB RR隔离级别下MVCC的可见性控制逻辑,需注意补充如下关键知识:
1. 事务ID并非事务begin时分配,是延迟到需要分配时才分配的。
2. 事务在首次快照读时创建快照,并将快照版本的可见范围控制信息记录在read_view对象中。
InnoDB MVCC RR隔离级别下的数据可见性总结的更多相关文章
- Innodb 中 RR 隔离级别能否防止幻读?
问题引出 我之前的一篇博客 数据库并发不一致分析 有提到过事务隔离级别以及相应加锁方式.能够解决的并发问题. 标准情况下,在 RR(Repeatable Read) 隔离级别下能解决不可重复读(当行修 ...
- [原创]MySQL RR隔离级别下begin或start transaction开启事务后的可重复读?
Server version: 5.6.21-log MySQL Community Server (GPL) 前提提要: 我们知道MySQL的RR(repeatable read)隔 ...
- RR隔离级别下通过next-key locks 避免幻影读
---恢复内容开始--- mysql innodb目前使用范围最广的两种隔离级别为RC和RR,RR修复了RC中所存在的不可重复读 READ COMMITED 不可重复读 在同一事务中两次查看的结果集不 ...
- mysql中不同事务隔离级别下数据的显示效果--转载
事务是一组原子性的SQL查询语句,也可以被看做一个工作单元.如果数据库引擎能够成功地对数据库应用所有的查询语句,它就会执行所有查询,如果任何一条查询语句因为崩溃或其他原因而无法执行,那么所有的语句就都 ...
- 浅谈mysql中不同事务隔离级别下数据的显示效果
事务的概念 事 务是一组原子性的SQL查询语句,也可以被看做一个工作单元.如果数据库引擎能够成功地对数据库应用所有的查询语句,它就会执行所有查询,如果任何一条查 询语句因为崩溃或其他原因而无法执行,那 ...
- mysql 开发进阶篇系列 12 锁问题(隔离级别下锁的差异)
1. innodb在不同隔离级别下的一致性读及锁的差异 不同的隔离级别下,innodb处理sql 时采用的一致性读策略和需要的锁是不同的,同时,数据恢复和复制机制的特点,也对一些sql的一致性读策略和 ...
- InnoDB在MySQL默认隔离级别下解决幻读
1.结论 在RR的隔离级别下,Innodb使用MVVC和next-key locks解决幻读,MVVC解决的是普通读(快照读)的幻读,next-key locks解决的是当前读情况下的幻读. 2.幻读 ...
- MySQL--REPEATABLE-READ隔离级别下读取到的“重复数据”
在MySQL中,使用MVCC来实现REPEATABLE-READ隔离级别,由于SELECT操作不会对数据加锁,其他回话可以修改当前回话所读取过的数据而不会被阻塞,因此读写不冲突. 在MVCC并发控制中 ...
- Mysql 间隙锁原理,以及Repeatable Read隔离级别下可以防止幻读原理(百度)
Mysql知识实在太丰富了,前几天百度的面试官问我MySql在Repeatable Read下面是否会有幻读出现,我说按照事务的特性当然会有, 但是面试官却说 Mysql 在Repeatable Re ...
随机推荐
- 利用C#查看特定服务是否安装
需求:想通过C#代码来查看IIS服务或者MSMQ是否已经安装 分析:IIS服务和MSMQ安装完成后都会创建windows服务,所以我们只需要查看对应的服务是否存在即可. 准备工作: IIS服务名称:W ...
- tesseract 3.05 release 编译
tesseract 3.05 release版本的对应配置好的vs2015工程.偷懒必备,毕竟依赖那么多库,环境配置还是要费点事的.https://github.com/peirick/VS2015_ ...
- pxe-kickstart批量部署文档
#PXE安装: yum install syslinux xinetd tftp-server httpd -y yum install dhcp -y yum install system-conf ...
- 20145219 《Java程序设计》实验四 Android开发基础设计实验报告
20145219 <Java程序设计>实验四 Android开发基础设计实验报告 实验内容 安装Andriod Studio并配置软件 使用Andriod Studio软件实现Hello ...
- bzoj 3479: [Usaco2014 Mar]Watering the Fields
3479: [Usaco2014 Mar]Watering the Fields Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 174 Solved ...
- [BZOJ1587]叶子合并leaves
Description 在一个美丽的秋天,丽丽每天都经过的花园小巷落满了树叶,她决定把树叶堆成K堆,小巷是笔直的 共有N片树叶(树叶排列也是笔直的),每片树叶都有一个重量值,并且每两片想邻的树叶之间的 ...
- scala学习手记13 - 类继承
在scala里,类继承有两点限制: 重写方法需要使用override关键字: 只有主构造函数才能往父类构造函数中传参数. 在java1.5中引入了override注解,但不强制使用.不过在scala中 ...
- C++ 函数后面的const
一个函数 AcGePoint3dstartPoint() const; const放在后面跟前面有区别么 ==> 准确的说const是修饰this指向的对象的 譬如,我们定义了 classA{ ...
- python学习笔记(time.time方法)
time.time()方法 返回的是 1970纪元后经过的浮点秒数 比较简单 总结如下: #!/usr/bin/env python # -*- coding: utf_8 -*- import ti ...
- django model_fields_validators 前端页面编辑自定义验证
# model_field_validators.py import re from django.core.exceptions import ValidationError from django ...