MySQL可重复读事务隔离具体是怎么实现的

news2024/10/5 5:17:21

 事务的启动会有的操作

  事务的隔离等级有四种,现在说默认的可重复读,可重复读就是一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的。

  可重复读隔离级别的事务启动的时候会创建一个视图 read-view,这个视图是不会改变的了,之后事务执行期间,即使有其他事务修改了数据,事务看到的仍然跟在启动时看到的一样。也就是说,一个在可重复读隔离级别下执行的事务,好像与世无争,不受外界影响。

  读提交的级别的事务会在每次执行sql语句前重新生成一个read-view。

 事务的启动时机

  正常情况下begin/start transaction 命令并不是一个事务的起点,事务的起点是他们之后第一个操作表的sql语句 

  但是你要立即启动一个事务可以用 start transaction with consistent snapshot 这个命令。

  第一种启动方式,一致性视图是在执行第一个快照读语句时创建的; 

  第二种启动方式,一致性视图是在执行 start transaction with consistent snapshot 时创建的。

---------------------------------------------------------------------------------------------------------------------------------

 实例 

 创建一个表 只有id和k

CREATE TABLE `t` (
`id` int(11) NOT NULL,
`k` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
insert into t(id, k) values(1,1),(2,2);

 进行下面三个事务。顺序依图1所示

  在这个例子中,事务 C 没有显式地使用 begin/commit,表示这个 update 语句本身就是一个事务,语句完成的时候会自动提交。事务 B 在更新了行之后查询 ; 事务 A 在一个只读事务中查询,并且时间顺序上是在事务 B 的查询之后。 

  这个答案是A中k是1,B中的k是3

下面将详细解释为什么会有这个结果。

在 MySQL 里,有两个“视图”的概念:

  一个是 view。它是一个用查询语句定义的虚拟表,在调用的时候执行查询语句并生成结果。创建视图的语法是 create view … ,而它的查询方法与表一样。

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

“快照”在 MVCC 里是怎么工作的?

在可重复读隔离级别下,事务在启动的时候就“拍了个快照”。注意,这个快照是基于整库的。

这时,你会说这看上去不太现实啊。如果一个库有 100G,那么我启动一个事务,MySQL 就要拷贝 100G 的数据出来,这个过程得多慢啊。可是,我平时的事务执行起来很快啊。 

快照是怎么实现的呢?

  InnoDB 里面每个事务有一个唯一的事务 ID,叫作 transaction id。它是在事务开始的时候向 InnoDB 的事务系统申请的,是按申请顺序严格递增的。

  而每行数据也都是有多个版本的。每次事务更新数据的时候,都会生成一个新的数据版本,并且把 transaction id 赋值给这个数据版本的事务 ID,记为 row trx_id。同时,旧的数据版本要保留,并且在新的数据版本中,能够有信息可以直接拿到它。

也就是说,数据表中的一行记录,其实可能有多个版本 (row),每个版本有自己的 row trx_id。 

如图2所示,就是一个记录被多个事务连续更新后的状态。

  图中虚线框里是同一行数据的 4 个版本,当前最新版本是 V4,k 的值是 22,它是被 transaction id 为 25 的事务更新的,因此它的 row trx_id 也是 25。 

图中的三个虚线箭头,就是 undo log(回滚日志:每个事务都会有回滚日志);而 V1、V2、V3 并不是物理上真实存在的,而是每次需要的时候根据当前版本和 undo log 计算出来的。比如,需要 V2 的时候,就是通过 V4 依次执行 U3、U2 算出来。

明白了多版本和 row trx_id 的概念后,我们再来想一下,InnoDB 是怎么定义那个“100G”的快照的。 

按照可重复读的定义,一个事务启动的时候,能够看到所有已经提交的事务结果。但是之后,这个事务执行期间,其他事务的更新对它不可见。

因此,一个事务只需要在启动的时候声明说,“以我启动的时刻为准,如果一个数据版本是在我启动之前生成的,就认;如果是我启动以后才生成的,我就不认,我必须要找到它的上一个版本”。

当然,如果“上一个版本”也不可见,那就得继续往前找。还有,如果是这个事务自己更新的数据,它自己还是要认的。

在实现上, InnoDB 为每个事务构造了一个数组,用来保存这个事务启动瞬间,当前正在“活跃”的所有事务 ID。“活跃”指的就是,启动了但还没提交。

数组里面事务 ID 的最小值记为低水位,当前系统里面已经创建过的事务 ID 的最大值加 1 记为高水位。 注意:是系统内的不是当前事务内的。

这个视图数组和高水位,就组成了当前事务的一致性视图(read-view)。

而数据版本的可见性规则,就是基于数据的 row trx_id 和这个一致性视图的对比结果得到的。

这个视图数组把所有的 row trx_id 分成了几种不同的情况。

                                                                                            图3 

这样,对于当前事务的启动瞬间来说,一个数据版本的 row trx_id,有以下几种可能:

1.如果落在绿色部分,表示这个版本是已提交的事务或者是当前事务自己生成的,这个数据是可见的;

2.如果落在红色部分,表示这个版本是由将来启动的事务生成的,是肯定不可见的;

3.如果落在黄色部分,那就包括两种情况:

    a. 若 row trx_id 在数组中,表示这个版本是由还没提交的事务生成的,不可见; 

    b. 若 row trx_id 不在数组中,表示这个版本是已经提交了的事务生成的,可见。

比如,对于图2中的数据来说,如果有一个事务,它的低水位是 18,那么当它访问这一行数据时,就会从 V4 通过 U3 计算出 V3,所以在它看来,这一行的值是 11。 

你看,有了这个声明后,系统里面随后发生的更新,是不是就跟这个事务看到的内容无关了呢?因为之后的更新,生成的版本一定属于上面的 2 或者 3(a) 的情况,而对它来说,这些新的数据版本是不存在的,所以这个事务的快照,就是“静态”的了。

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

接下来,我们继续看一下图 1 中的三个事务,分析下事务 A 的语句返回的结果,为什么是 k=1。 

这里,我们不妨做如下假设:

事务 A 开始前,系统里面只有一个活跃事务 ID 是 99; 

事务 A、B、C 的版本号分别是 100、101、102,且当前系统里只有这四个事务;

三个事务开始前,(1,1)这一行数据的 row trx_id 是 90。

这样,事务 A 的视图数组就是 [99,100], 事务 B 的视图数组是 [99,100,101], 事务 C 的视图数组是 [99,100,101,102]。

为了简化分析,我先把其他干扰语句去掉,只画出跟事务 A 查询逻辑有关的操作:

                                                                                          图4

从图中可以看到,第一个有效更新是事务 C,把数据从 (1,1) 改成了 (1,2)。这时候,这个数据的最新版本的 row trx_id 是 102,而 90 这个版本已经成为了历史版本。

第二个有效更新是事务 B,把数据从 (1,2) 改成了 (1,3)。这时候,这个数据的最新版本(即 row trx_id)是 101,而 102 又成为了历史版本。

你可能注意到了,在事务 A 查询的时候,其实事务 B 还没有提交,但是它生成的 (1,3) 这个版本已经变成当前版本了。但这个版本对事务 A 必须是不可见的,否则就变成脏读了。

好,现在事务 A 要来读数据了,它的视图数组是 [99,100],高水位是101(事务开始就建立了read-view所以的它的高水位就是当前系统最大的id100+1,A是最先开始的所有当时的系统中只有99和100两个id所以是100+1)。当然了,读数据都是从当前版本读起的。所以,事务 A 查询语句的读数据流程是这样的:

找到 (1,3) 的时候,判断出 row trx_id=101,等于高水位,处于红色区域,不可见;

接着,找到上一个历史版本,一看 row trx_id=102,比高水位大,处于红色区域,不可见;

再往前找,终于找到了(1,1),它的 row trx_id=90,比低水位小,处于绿色区域,可见。

这样执行下来,虽然期间这一行数据被修改过,但是事务 A 不论在什么时候查询,看到这行数据的结果都是一致的,所以我们称之为一致性读。

这个判断规则是从代码逻辑直接转译过来的,但是正如你所见,用于人肉分析可见性很麻烦。

所以,我来给你翻译一下。一个数据版本,对于一个事务视图来说,除了自己的更新总是可见以外,有三种情况:

版本未提交,不可见;

版本已提交,但是是在视图创建后提交的,不可见;

版本已提交,而且是在视图创建前提交的,可见。 

现在,我们用这个规则来判断图 4 中的查询结果,事务 A 的查询语句的视图数组是在事务 A 启动的时候生成的,这时候:

(1,3) 还没提交,属于情况 1,不可见;

(1,2) 虽然提交了,但是是在视图数组创建之后提交的,属于情况 2,不可见;

(1,1) 是在视图数组创建之前提交的,可见。 

更新逻辑

事务 B 的 update 语句,如果按照一致性读,好像结果不对哦?

你看图 5 中,事务 B 的视图数组是先生成的,之后事务 C 才提交,不是应该看不见 (1,2) 吗,怎么能算出 (1,3) 来?

                                                                                                图5

是的,如果事务 B 在更新之前查询一次数据,这个查询返回的 k 的值确实是 1。 

但是,当它要去更新数据的时候,就不能再在历史版本上更新了,否则事务 C 的更新就丢失了。因此,事务 B 此时的 set k=k+1 是在(1,2)的基础上进行的操作。

所以,这里就用到了这样一条规则:更新数据都是先读后写的,而这个读,只能读当前的值,称为“当前读”(current read)。

因此,在更新的时候,当前读拿到的数据是 (1,2),更新后生成了新版本的数据 (1,3),这个新版本的 row trx_id 是 101。 

所以,在执行事务 B 查询语句的时候,一看自己的版本号是 101,最新数据的版本号也是 101,是自己的更新,可以直接使用,所以查询得到的 k 的值是 3。                                        

至此解决了图1的问题   

事务C先执行并提交此时C中的k为2,然后事务B中如果只有select查询的时候事务B中k也是1,但是如果有update,根据更新逻辑所以在updata操作中k是2,所以事务B中的k经过加1操作后是3,事务B中在update之后的select查询就是事务B自身修改后的数据了k也是3,然后事务A在查询的时候就是查询自己最初建立read-view时候的数据,所以k为1。结果就是事务A中的k=1,事务B中的k=3,事务C中的k=2.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/421579.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java阶段一Day22

Java阶段一Day22 文章目录Java阶段一Day22线程安全synchronized教师总结新单词多线程多线程并发安全问题概念例synchronized关键字同步方法同步块在静态方法上使用synchronized互斥锁总结重点:多线程并发安全问题聊天室(续)实现服务端发送消息给客户端服务端转发消息给所有客户…

内网穿透实现在外远程连接RabbitMQ服务

文章目录前言1.安装erlang 语言2.安装rabbitMQ3. 内网穿透3.1 安装cpolar内网穿透(支持一键自动安装脚本)3.2 创建HTTP隧道4. 公网远程连接5.固定公网TCP地址5.1 保留一个固定的公网TCP端口地址5.2 配置固定公网TCP端口地址转载自远控源码文章:无公网IP,…

Linux Systemd type=simple和type=forking的区别

Typeforking 使用Typeforking时,要求ExecStart启动的命令自身就是以daemon模式运行的。 而以daemon模式运行的进程都有一个特性:总是会有一个瞬间退出的中间父进程,例如,nginx命令默认以daemon模式运行,所以可直接将其…

Nodejs vm/vm2沙箱逃逸

文章目录什么是沙箱以及VM?vm模块nodejs作用域vm沙箱vm沙箱逃逸vm2例题分析:(待补充)[HFCTF2020]JustEscape[HZNUCTF 2023 final]eznode參考文章:什么是沙箱以及VM? 什么是沙箱: 沙箱就是能够像一个集装箱…

Ansys Speos | 联合 optiSLang 背光板设计优化方案

在这个例子中,讲述如何建模一个典型的背光单元及其与亮度和均匀性有关的照度分布。其中一个关键特点是使用了Speos 3D Texture功能,这是最初开发的用于背光单元产品,并可用于设计导光板,亮度增强膜(BEF)和由数千/数百万组成的背光…

《程序员面试金典(第6版)》面试题 10.03. 搜索旋转数组(二分法,分钟思想,入门题目)

题目描述 搜索旋转数组。给定一个排序后的数组,包含n个整数,但这个数组已被旋转过很多次了,次数不详。请编写代码找出数组中的某个元素,假设数组元素原先是按升序排列的。若有多个相同元素,返回索引值最小的一个。 示例…

C学习笔记2

1、二进制由 0 和 1 两个数字组成,使用时必须以0b或0B(不区分大小写)开头 2、符号位进制形式进制数据 (进制形式决定后面的数据是哪种进制) 3、合法的二进制 int a 0b101; // 0b是二进制的进制形式 101是进制…

buildroot使用外部编译链编译bluez蓝牙工具

在开发ublox w263 wifi蓝牙时,之前是使用yocto系统集成编译出的bluez工具,减少了自己编译工具软件和依赖库的工作,切换项目使用原生linux系统后,所以的软件需要自己编译,不想编译每个依赖文件和库,所以使用…

Pytorch深度学习笔记(三)线性模型

目录 1.机械学习的过程 2.线性模型 推荐课程:2.线性模型_哔哩哔哩_bilibili 1.机械学习的过程 机械学习的过程: 1.准备数据集DataSet——>2.选择模型Model——>3.训练Training——>4.推理Infering 监督学习:用已知标签的训练样本训…

Spark大数据处理讲课笔记3.1 掌握RDD的创建

文章目录零、本节学习目标一、RDD为何物(一)RDD概念(二)RDD示例(三)RDD主要特征二、做好准备工作(一)准备文件1、准备本地系统文件2、启动HDFS服务3、上传文件到HDFS(二&…

4年软件测试工作经验,跳槽之后面试20余家公司的总结

先说一下自己的个人情况,普通二本计算机专业毕业,懂python,会写脚本,会selenium,会性能,然而离职后到今天都没有收到一份offer!一直在待业中,从离职第一天就开始准备简历&#xff0c…

快排的非递归实现

其思想与递归实现快排完全相同,可以先将第一次要排序的右边界和左边界先后入栈,然后判断栈 是否为空,不为空就出栈顶元素,并删除一次,由于栈是先进的后出,所以先出来的应该是左界, 再进行一次…

【C++】STL——unordered_map和unordered_set的介绍和使用

unordered_set和unordered_map的介绍和使用 文章目录unordered_set和unordered_map的介绍和使用一、unordered系列关联式容器二、unordered_set1.unordered_set的介绍2.unordered_set的构造方式3.unordered_set的函数接口说明4.unordered_multiset的介绍及使用三、unordered_ma…

【建议收藏】数据库 SQL 入门——约束(内附演示)

文章目录📚引言📖约束📑非空约束📑唯一约束📑主键约束📑默认约束📑检查约束📑外键约束🔖外键的添加🔖删除/更新外键📍总结📚引言 &…

【高危】Apache Linkis Gateway模块存在身份验证绕过漏洞(CVE-2023-27987)

漏洞描述 Apache Linkis 是一个用于将上层应用与底层数据引擎解耦,提供标准化接口的中间件。Gateway 是 Linkis 接受客户端和外部请求的主要入口点, 在 Apache Linkis 受影响版本中,由于在 Linkis Gateway 部署时产生的Token默认为LINKIS_C…

Linux打印口/LPT口出厂测试工具与使用说明

1 软件功能 该软件用于在Linux平台测试CH35X/CH38X(PCI/PCIe转串并口)的并口各引脚功能是否正常。方便对设备进行出厂测试。 2 并口测试硬件治具 在测试前,需要制作单独的硬件治具,按下表连接信号线: 25针并口座子堵…

面向削峰填谷的电动汽车多目标优化调度策略

说明书 MATLAB代码:面向削峰填谷的电动汽车多目标优化调度策略 关键词:电动汽车 削峰填谷 多目标 充放电优化 参考文档:店主自己整理的说明文档,公式、约束、数据齐全,可联系我查看 仿真平台:MATLAB YA…

iPhone如何不用iTunes将视频传输到电脑上?

随着智能手机的普及,iPhone已经成为了人们生活中必不可少的一部分。而随着iPhone摄像功能的逐渐完善,越来越多的用户开始将iPhone作为拍摄视频的工具。 但是,将iPhone中的视频传输到电脑并进行后续编辑处理或者备份储存,对于许多…

二极管专题:二极管钳位电路

二极管钳位电路 之前我们说过二极管的限幅功能 二极管专题:限幅电路。今天说的二极管的钳位电路和二极管的限幅电路都是利用了二极管正向压降一定的这么一个特点。限幅电路和钳位电路你说区别大呢,它也不大,说小呢也不小。就看你怎么理解了&…

Linux proc文件系统介绍

Linux proc文件系统 /proc/cmdline Arguments passed to the Linux kernel at boot time. Often done via a boot manager such as lilo(8) or grub(8) proc/[pid]/coredump_filter Since kernel 2.6.23, the Linux-specific /proc/PID/coredump_filter file can be used …