MySQL - 事务隔离级别

MySQL 事务

本文所说的 MySQL 事务都是指在 InnoDB 引擎下,MyISAM 引擎是不支持事务的。

数据库事务指的是一组数据操作,事务内的操作要么就是全部成功,要么就是全部失败

事务具有原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)四个特性,简称 ACID,缺一不可。

今天要说的就是隔离性


概念说明

以下几个概念是事务隔离级别要实际解决的问题,所以需要搞清楚都是什么意思。

脏读

脏读指的是读到了其他事务未提交的数据,未提交意味着这些数据可能会回滚,也就是可能最终不会存到数据库中,也就是不存在的数据。读到了并一定最终存在的数据,这就是脏读。(别的事务来骗,来偷袭!这数据是假的)

可重复读

可重复读指的是在一个事务内,最开始读到的数据和事务结束前的任意时刻读到的同一批数据都是一致的。通常针对数据更新(UPDATE)操作。

不可重复读

对比可重复读,不可重复读指的是在同一事务内,不同的时刻读到的同一批数据可能是不一样的,可能会受到其他事务的影响,比如其他事务改了这批数据并提交了。通常针对数据更新(UPDATE)操作。(我刚刚读的好像不是这样的?)

幻读

幻读是针对数据插入(INSERT)操作来说的。

假设事务A对某些行的内容作了更改,但是还未提交

此时事务B插入了与事务A更改前的记录相同的记录行,并且在事务A提交之前先提交了

而这时,在事务A中查询,会发现好像刚刚的更改对于某些数据未起作用,但其实是事务B刚插入进来的,让用户感觉很魔幻,感觉出现了幻觉,这就叫幻读。(我刚刚明明把表中这个z种类的产品都加价10元了的啊?)

/同一个事务中以前没有的行,由于其他事务的提交而出现的新行。


事务隔离级别

SQL 标准定义了四种隔离级别,MySQL 全都支持。这四种隔离级别分别是:

  • 读未提交(READ UMCOMMITTED) 允许一个事务可以看到其他事务未提交的修改。
  • 读已提交(READ COMMITTED) 允许一个事务只能看到其他事务已经提交的修改,未提交的修改是不可见的。
  • 可重复读(REPEATABLE READ) 确保如果在一个事务中执行两次相同的SELECT语句,都能得到相同的结果,不管其他事务是否提交这些修改。
  • 串行化 (SERIALIZABLE)

从上往下,隔离强度逐渐增强,性能逐渐变差。采用哪种隔离级别要根据系统需求权衡决定,其中,可重复读是 MySQL 的默认级别。

事务隔离其实就是为了解决上面提到的脏读、不可重复读、幻读这几个问题,下面展示了 4 种隔离级别对这三个问题的解决程度。(MySQL在可重复读级别解决了幻读问题)

读未提交和串行化基本上是不需要考虑的隔离级别,前者不加锁限制,后者相当于单线程执行,效率太差。

MySQL 在可重复读级别解决了幻读问题,是通过行锁和间隙锁的组合 Next-Key 锁实现的。


实例

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(30) DEFAULT NULL,
  `age` tinyint(4) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
mysql> SELECT * FROM user;
+----+-----------------+------+
| id | name            | age  |
+----+-----------------+------+
|  1 | 古时的风筝        |    1 |
+----+-----------------+------+

读未提交

MySQL 事务隔离其实是依靠锁来实现的,加锁自然会带来性能的损失。而读未提交隔离级别是不加锁的,所以它的性能是最好的,没有加锁、解锁带来的性能开销。但有利就有弊,这基本上就相当于裸奔啊,所以它连脏读的问题都没办法解决。

任何事务对数据的修改都会第一时间暴露给其他事务,即使事务还没有提交。

启动两个事务,分别为事务A和事务B,在事务A中使用 update 语句,修改 age 的值为10,初始是1 ,在执行完 update 语句之后,在事务B中查询 user 表,会看到 age 的值已经是 10 了,这时候事务A还没有提交,而此时事务B有可能拿着已经修改过的 age=10 去进行其他操作了。在事务B进行操作的过程中,很有可能事务A由于某些原因,进行了事务回滚操作,那其实事务B得到的就是脏数据了,拿着脏数据去进行其他的计算,那结果肯定也是有问题的。

 


读提交

既然读未提交没办法解决脏数据问题,那么就有了读提交。读提交就是一个事务只能读到其他事务已经提交过的数据,也就是其他事务调用 commit 命令之后的数据。那脏数据问题迎刃而解了。

读提交事务隔离级别是大多数流行数据库的默认事务隔离界别,比如 Oracle,但是不是 MySQL 的默认隔离界别。

同样开启事务A和事务B两个事务,在事务A中使用 update 语句将 id=1 的记录行 age 字段改为 10。此时,在事务B中使用 select 语句进行查询,我们发现在事务A提交之前,事务B中查询到的记录 age 一直是1,直到事务A提交,此时在事务B中 select 查询,发现 age 的值已经是 10 了。

这就出现了一个问题,在同一事务中(本例中的事务B),事务的不同时刻同样的查询条件,查询出来的记录内容是不一样的,事务A的提交影响了事务B的查询结果,这就是不可重复读,也就是读提交隔离级别。

每个 select 语句都有自己的一份快照,而不是一个事务一份,所以在不同的时刻,查询出来的数据可能是不一致的。

读提交解决了脏读的问题,但是无法做到可重复读,也没办法解决幻读。


可重复读

可重复是对比不可重复而言的,上面说不可重复读是指同一事物不同时刻读到的数据值可能不一致。

而可重复读是指,事务不会读到其他事务对已有数据的修改,即使其他事务已提交,也就是说,事务开始时读到的已有数据是什么,在事务提交前的任意时刻,这些数据的值都是一样的。

但是,对于其他事务新插入的数据是可以读到的,这也就存在着幻读问题。

在这个隔离级别下,启动两个事务,两个事务同时开启。

首先看一下可重复读的效果,事务A启动后修改了数据,并且在事务B之前提交,事务B在事务开始和事务A提交之后两个时间节点都读取的数据相同,已经可以看出可重复读的效果。

可重复读做到了,这只是针对已有行的更改操作有效,但是对于新插入的行记录,就没这么幸运了,幻读就这么产生了。我们看一下这个过程:

事务A开始后,执行 update 操作,将 age = 1 的记录的 name 改为“风筝2号”;

事务B开始后,在事务执行完 update 后,执行 insert 操作,插入记录 age =1,name = 古时的风筝,这和事务A修改的那条记录值相同,然后提交。

事务B提交后,事务A中执行 select,查询 age=1 的数据,这时,会发现多了一行,并且发现还有一条 name = 古时的风筝,age = 1 的记录,这其实就是事务B刚刚插入的,这就是幻读。

在 MySQL 中测试幻读的时候,并不会出现上图的结果,幻读并没有发生,MySQL 的可重复读隔离级别其实解决了幻读问题

 

 

 


MySQL 中是如何实现事务隔离的

首先说读未提交,它是性能最好,也可以说它是最野蛮的方式,因为它压根儿就不加锁,所以根本谈不上什么隔离效果,可以理解为没有隔离。

再来说串行化。读的时候加共享锁,也就是其他事务可以并发读,但是不能写。写的时候加排它锁,其他事务不能并发写也不能并发读。

最后说读提交和可重复读。这两种隔离级别是比较复杂的,既要允许一定的并发,又想要兼顾的解决问题。


MVVC - 实现可重复读

为了解决不可重复读,或者为了实现可重复读,MySQL 采用了 MVVC (多版本并发控制) 的方式。

我们在数据库表中看到的一行记录可能实际上有多个版本,每个版本的记录除了有数据本身外,还要有一个表示版本的字段,记为 row trx_id,而这个字段就是使其产生的事务的 id,事务 ID 记为 transaction id,它在事务开始的时候向事务系统申请,按时间先后顺序递增。

按照上面这张图理解,一行记录现在有 3 个版本,每一个版本都记录这使其产生的事务 ID,比如事务A的transaction id 是100,那么版本1的row trx_id 就是 100,同理版本2和版本3。

在上面介绍读提交和可重复读的时候都提到了一个词,叫做快照,学名叫做一致性视图,这也是可重复读和不可重复读的关键,可重复读是在事务开始的时候生成一个当前事务全局性的快照,而读提交则是每次执行语句的时候都重新生成一次快照。

对于一个快照来说,它能够读到那些版本数据,要遵循以下规则:

  1. 当前事务内的更新,可以读到;
  2. 版本未提交,不能读到;
  3. 版本已提交,但是却在快照创建后提交的,不能读到;
  4. 版本已提交,且是在快照创建前提交的,可以读到;

利用上面的规则,再返回去套用到读提交和可重复读的那两张图上就很清晰了。

两者主要的区别就是在快照的创建上,可重复读仅在事务开始是创建一次,而读提交每次执行语句的时候都要重新创建一次。


行锁 - 并发写问题

存在这的情况,两个事务,对同一条数据做修改。最后结果应该是是时间靠后的那个。并且更新之前要先读数据,这里所说的读和上面说到的读不一样,更新之前的读叫做“当前读”,总是当前版本的数据,也就是多版本中最新一次提交的那版。

假设事务A执行 update 操作, update 的时候要对所修改的行加行锁,这个行锁会在提交之后才释放。而在事务A提交之前,事务B也想 update 这行数据,于是申请行锁,但是由于已经被事务A占有,事务B是申请不到的,此时,事务B就会一直处于等待状态,直到事务A提交,事务B才能继续执行,如果事务A的时间太长,那么事务B很有可能出现超时异常。如下图所示。

加锁的过程要分有索引和无索引两种情况,比如下面这条语句

update user set age=11 where id = 1

id 是这张表的主键,是有索引的情况,那么 MySQL 直接就在索引数中找到了这行数据,然后干净利落的加上行锁就可以了。

而下面这条语句

update user set age=11 where age=10

表中并没有为 age 字段设置索引,所以, MySQL 无法直接定位到这行数据。那怎么办呢,当然也不是加表锁了。MySQL 会为这张表中所有行加行锁,没错,是所有行。但是呢,在加上行锁后,MySQL 会进行一遍过滤,发现不满足的行就释放锁,最终只留下符合条件的行。虽然最终只为符合条件的行加了锁,但是这一锁一释放的过程对性能也是影响极大的。所以,如果是大表的话,建议合理设计索引,如果真的出现这种情况,那很难保证并发度。


行锁和间隙锁合并(Next-Key锁) - 解决幻读

上面介绍可重复读的时候,那张图里标示着出现幻读的地方实际上在 MySQL 中并不会出现,MySQL 已经在可重复读隔离级别下解决了幻读的问题。

前面刚说了并发写问题的解决方式就是行锁,而解决幻读用的也是锁,叫做间隙锁,MySQL 把行锁和间隙锁合并在一起,解决了并发写和幻读的问题,这个锁叫做 Next-Key锁。

假设现在表中有两条记录,并且 age 字段已经添加了索引,两条记录 age 的值分别为 10 和 30。

 

此时,在数据库中会为索引维护一套B+树,用来快速定位行记录。B+索引树是有序的,所以会把这张表的索引分割成几个区间。

如图所示,分成了3 个区间,(负无穷,10]、(10,30]、(30,正无穷],在这3个区间是可以加间隙锁的。

下面的两个事务演示一下加锁过程。

在事务A提交之前,事务B的插入操作只能等待,这就是间隙锁起得作用。当事务A执行update user set name='风筝2号’ where age = 10; 的时候,由于条件 where age = 10 ,数据库不仅在 age =10 的行上添加了行锁,而且在这条记录的两边,也就是(负无穷,10]、(10,30]这两个区间加了间隙锁,从而导致事务B插入操作无法完成,只能等待事务A提交。不仅插入 age = 10 的记录需要等待事务A提交,age<10、10<age<30 的记录页无法完成,而大于等于30的记录则不受影响,这足以解决幻读问题了。

这是有索引的情况,如果 age 不是索引列,那么数据库会为整个表加上间隙锁。所以,如果是没有索引的话,不管 age 是否大于等于30,都要等待事务A提交才可以成功插入。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

软件压力测试的重要性与用途

在当今数字化的时代&#xff0c;软件已经成为几乎所有行业不可或缺的一部分。随着软件应用规模的增加和用户数量的上升&#xff0c;软件的性能变得尤为关键。为了确保软件在面对高并发和大负载时仍然能够保持稳定性和可靠性&#xff0c;软件压力测试变得至关重要。下面是软件压…

vue2-elementUI部分组件样式修改

el-radio样式&#xff1a; /deep/ .el-radio__input .el-radio__inner {width: 20px;height: 20px;position: relative;cursor: pointer;-webkit-appearance: none;-moz-appearance: none;appearance: none;border: 1px solid #999;border-radius: 0;outline: none;transition…

LeetCode刷题--- 二叉树剪枝

个人主页&#xff1a;元清加油_【C】,【C语言】,【数据结构与算法】-CSDN博客 个人专栏&#xff1a;http://t.csdnimg.cn/ZxuNL http://t.csdnimg.cn/c9twt 前言&#xff1a;这个专栏主要讲述递归递归、搜索与回溯算法&#xff0c;所以下面题目主要也是这些算法做的 我讲述…

渗透测试是什么

随着信息技术的飞速发展&#xff0c;网络安全问题日益凸显。其中&#xff0c;渗透测试作为一种重要的安全评估方法&#xff0c;已经被越来越多的企业和组织所采用。渗透测试通过模拟黑客攻击&#xff0c;发现并修复潜在的安全漏洞&#xff0c;从而提高系统的安全性。 直白的说…

题目:肖恩的苹果林(蓝桥OJ 3683)

题目描述&#xff1a; 解题思路&#xff1a; 本题采用二分中的二分答案。枚举每一个最大距离&#xff08;i&#xff1a;1 ~ n&#xff09;以及他们至少能容纳的树木数&#xff08;上一题&#xff1a;跳石头-蓝桥OJ 364&#xff09; 判断二分内判断条件是>还是<以及是lmi…

满载re:Invent 2023全新发布惊喜,亚马逊云科技下一站GenAI@巡演来啦

无限构建&#xff0c;成为生成式AI原生开发者前沿生成式AI技术之旅正式启程&#xff0c;穿越多个中国的城市&#xff0c;开发者一站式体验&#xff0c;满载re:Invent 2023全新发布惊喜。 LET’S Demo 「构」硬核 生成式AI时代的开发新范式 Amazon Q 全新的企业级生成式AI助手…

CNN、LeNet、AlexNet基于MNIST数据集进行训练和测试,并可视化对比结果

完成内容&#xff1a; 构建CNN并基于MNIST数据集进行训练和测试构建LeNet并基于MNIST数据集进行训练和测试构建AlexNet并基于MNIST数据集进行训练和测试对比了不同网络在MNIST数据集上训练的效果 准备工作 import torch import torch.nn as nn import torch.optim as optim …

【Canvas】记录一次从0到1绘制风场空间分布图的过程

前言 &#x1f4eb; 大家好&#xff0c;我是南木元元&#xff0c;热衷分享有趣实用的文章&#xff0c;希望大家多多支持&#xff0c;一起进步&#xff01; &#x1f345; 个人主页&#xff1a;南木元元 目录 背景 前置知识 风场数据 绘制风场 准备工作 生成二维网格 获取…

vxe-table 右键菜单+权限控制(v3)

1.menu-config 是用于配置右键菜单的属性。通过 menu-config 属性&#xff0c;定义右键菜单的内容、显示方式和样式。 通过 menu-config 属性配置了右键菜单&#xff0c;其中的 options 属性定义了右键菜单的选项。用户在表格中右键点击时&#xff0c;将会弹出包含这些选项的自…

md笔记使用加自动备份整理

1、安装使用 TyporaGiteePicGo搭建图床&#xff08;解决使用Typora写的笔记上传csdn图片无法正常显示问题&#xff09; 2、修改主题 文件-》偏好设置-》外观-》打开主题文件夹 将css文件放到里面然后重启typora&#xff08;css文件可以参考参考链接&#xff09; 3、设置自动备份…

数据结构二维数组计算题,以行为主?以列为主?

1.假设以行序为主序存储二维数组Aarray[1..100,1..100]&#xff0c;设每个数据元素占2个存储单元&#xff0c;基地址为10&#xff0c;则LOC[5,5]&#xff08; &#xff09;。 A&#xff0e;808 B&#xff0e;818 C&#xff0e;1010 D&…

包装效果图渲染技巧:怎么用云渲染省钱、省时间

在今天这个市场竞争白热化的时代&#xff0c;一个产品的包装设计往往决定了它在架上是否能够脱颖而出。因此&#xff0c;品牌在推向市场前精心设计的包装效果图显得尤为重要。在这里&#xff0c;我们将探究包装效果图渲染的关键性、渲染技巧及云渲染技术如何在提升渲染品质与降…

Matlab 点云收缩L1中值(Weiszfeld算法)

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 对于之前的加权均值收缩方式,它存在一个很大的缺点,即容易受到噪声的影响,因此这里我们采用另一种统计学方案:L1中值。其形式如下所示: 其中 x i x_i

DICOM 文件中,VR,VL,Sequence,图像二进制的几个注意点

DICOM 文件中&#xff0c;VR&#xff0c;VL&#xff0c;Sequence&#xff0c;图像二进制的几个注意点 1. 传输语法 DICOM 文件的结构&#xff0c;在网上有很多的学习资料&#xff0c;这里只介绍些容易混淆的概念&#xff0c;作为回看笔记。 每个传输语法&#xff0c;起都是表…

openGauss学习笔记-154 openGauss 数据库运维-备份与恢复-闪回恢复

文章目录 openGauss学习笔记-154 openGauss 数据库运维-备份与恢复-闪回恢复154.1 闪回查询154.1.1 背景信息154.1.2 前提条件154.1.3 语法154.1.4 参数说明154.1.5 使用示例 154.2 闪回表154.2.1 背景信息154.2.2 前提条件154.2.3 语法154.2.4 使用示例 154.3 闪回DROP/TRUNCA…

【清晰明了】Jenkins邮件发送配置

自带邮件插件 首先要知道的是jenkins是自带邮件插件的&#xff0c;且不支持卸载。 下面开始配置自带邮件插件。 配置默认邮件管理员 系统管理 --> 系统配置&#xff0c;进行如下配置&#xff1a; 不配置管理员邮件地址报错如下 jakarta.mail.internet.AddressException:…

网络编程----select 模型总结

为什么要使用select模型&#xff1f; 答&#xff1a;解决基本C/S模型中&#xff0c;accept()、recv()、send()阻塞的问题 select模型与C/S模型的不同点 C/S模型中accept()会阻塞一直傻等socket来链接select模型只解决accept()傻等的问题&#xff0c;不解决recv(),send()执行…

大厂大数据面试题收录(1)

目录 1.java 中 object 类有哪些方法? 2.说一下和equals的区别&#xff1f; 3.为什么要重写 equals 和 hashcode()方法&#xff1f; 4.机器学习中&#xff0c;监督学习 和 无监督学习的区别是啥&#xff1f;&#xff1f; 5.kafka 组件熟悉吗,kafka 如何实现消息的有序的&a…

css 表示具有特定类或者其他属性的某种标签类型的元素

需求 通过 css 选择器获取某种标签&#xff08;如&#xff1a;div、input 等&#xff09;具有某个属性&#xff08;如&#xff1a;class、id 等&#xff09;的元素&#xff0c;从而修改其样式。 代码 通过 [标签].[属性] 的方式来获取 <div class"test">&l…

C++相关闲碎记录(8)

1、预定义的Function adapter 和 binder #include <iostream> #include <functional>int main() {auto plus10 std::bind(std::plus<int>(), std::placeholders::_1, 10);std::cout << "10: " << plus10(6) << std::endl…