保姆级教程,终于搞懂脏读、幻读和不可重复读了!

be02d9e5dcd49673b0636796bbce91bc.png

作者 | 王磊

来源 | Java中文社群(ID:javacn666)

转载请联系授权(微信ID:GG_Stone)

我的文章合集:https://gitee.com/mydb/interview

在 MySQL 中事务的隔离级别有以下 4 种:

  1. 读未提交(READ UNCOMMITTED)

  2. 读已提交(READ COMMITTED)

  3. 可重复读(REPEATABLE READ)

  4. 序列化(SERIALIZABLE)

MySQL 默认的事务隔离级别是可重复读(REPEATABLE READ),这 4 种隔离级别的说明如下。

1.READ UNCOMMITTED

读未提交,也叫未提交读,该隔离级别的事务可以看到其他事务中未提交的数据。该隔离级别因为可以读取到其他事务中未提交的数据,而未提交的数据可能会发生回滚,因此我们把该级别读取到的数据称之为脏数据,把这个问题称之为脏读。

2.READ COMMITTED

读已提交,也叫提交读,该隔离级别的事务能读取到已经提交事务的数据,因此它不会有脏读问题。但由于在事务的执行中可以读取到其他事务提交的结果,所以在不同时间的相同 SQL 查询中,可能会得到不同的结果,这种现象叫做不可重复读。

3.REPEATABLE READ

可重复读,是 MySQL 的默认事务隔离级别,它能确保同一事务多次查询的结果一致。但也会有新的问题,比如此级别的事务正在执行时,另一个事务成功的插入了某条数据,但因为它每次查询的结果都是一样的,所以会导致查询不到这条数据,自己重复插入时又失败(因为唯一约束的原因)。明明在事务中查询不到这条信息,但自己就是插入不进去,这就叫幻读 (Phantom Read)。

4.SERIALIZABLE

序列化,事务最高隔离级别,它会强制事务排序,使之不会发生冲突,从而解决了脏读、不可重复读和幻读问题,但因为执行效率低,所以真正使用的场景并不多。

简单总结一下,MySQL 的 4 种事务隔离级别对应脏读、不可重复读和幻读的关系如下:

事务隔离级别脏读不可重复读幻读
读未提交(READ UNCOMMITTED)
读已提交(READ COMMITTED)×
可重复读(REPEATABLE READ)××
串行化(SERIALIZABLE)×××

只看以上概念会比较抽象,接下来,咱们一步步通过执行的结果来理解这几种隔离级别的区别。

前置知识

1.事务相关的常用命令

# 查看 MySQL 版本
select version();# 开启事务
start transaction;# 提交事务
commit;# 回滚事务
rollback;

2.MySQL 8 之前查询事务的隔离级别

查看全局 MySQL 事务隔离级别和当前会话的事务隔离级别的 SQL 如下:

select @@global.tx_isolation,@@tx_isolation;

以上 SQL 执行结果如下图所示:e34be6196f20bc227bf0d900f7ca280c.png

3.MySQL 8 之后查询事务的隔离级别

select @@global.transaction_isolation,@@transaction_isolation;

4.查看连接的客户端详情

每个 MySQL 命令行窗口就是一个 MySQL 客户端,每个客户端都可以单独设置(不同的)事务隔离级别,这也是演示 MySQL 并发事务的基础。以下是查询客户端连接的 SQL 命令:

show processlist;

以上 SQL 执行结果如下:fd23b28c4bac34580ce7e5ac5de4ecbc.png

5.查询连接客户端的数量

可以使用以下 SQL 命令,查询连当前接 MySQL 服务器的客户端数量:

show status like 'Threads%';

以上 SQL 执行结果如下:f1e42c420dab6b0cd2e18c6f2081f06c.png

6.设置客户端的事务隔离级别

通过以下 SQL 可以设置当前客户端的事务隔离级别:

set session transaction isolation level 事务隔离级别;

事务隔离级别的值有 4 个:READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ、SERIALIZABLE。

7.新建数据库和测试数据

创建测试数据库和表信息,执行 SQL 如下:

-- 创建数据库
drop database if exists testdb;
create database testdb;
use testdb;
-- 创建表
create table userinfo(id int primary key auto_increment,name varchar(250) not null,balance decimal(10,2) not null default 0
);
-- 插入测试数据
insert into userinfo(id,name,balance) values(1,'Java',100),(2,'MySQL',200);

创建的表结构和数据如下:6a802ce70500997a2101938c3d2f6a77.png

8.名称约定

接下来会使用两个窗口(两个客户端)来演示事务不同隔离级别中脏读、不可重复读和幻读的问题。其中左边的黑底绿字的客户端下文将使用“窗口 1”来指代,而右边的蓝底白字的客户端下文将用“窗口 2”来指代,如下图所示:2ddb1cc7c18060900777dddb834279ad.png

脏读

一个事务读到另外一个事务还没有提交的数据,称之为脏读。脏读演示的执行流程如下:

执行步骤客户端1(窗口1)客户端2(窗口2)说明
第 1 步
set session transaction isolation level read uncommitted;
start transaction;
select * from userinfo;
设置事务隔离级别为读未提交;
开启事务;
查询用户列表,其中 Java 用户的余额为 100 元。
第 2 步start transaction;
update userinfo set balance=balance+50 where name='Java';

开启事务;
给 Java 用户的账户加 50 元;
第 3 步
select * from userinfo;查询用户列表,其中 Java 用户的余额变成了 150 元。

脏读演示步骤1

设置窗口 2 的事务隔离级别为读未提交,设置命令如下:

set session transaction isolation level read uncommitted;

PS:事务隔离级别读未提交存在脏读的问题。

然后使用命令来检查当前连接窗口的事务隔离界别,如下图所示:98664929a5c07e769a2ab07d32aa99e4.png开启事务并查询用户列表信息,如下图所示:250d972366b12a6bf5f6065db9fe0623.png

脏读演示步骤2

在窗口 1 中开启一个事务,并给 Java 账户加 50 元,但不提交事务,执行的 SQL 如下:afffc50acc09b7e15528af91fbe70b0b.png

脏读演示步骤3

在窗口 2 中再次查询用户列表,执行结果如下:d17569b54ee098c507952acc2d144281.png从上述结果可以看出,在窗口 2 中读取到了窗口 1 中事务未提交的数据,这就是脏读。

不可重复读

不可重复读是指一个事务先后执行同一条 SQL,但两次读取到的数据不同,就是不可重复读。不可重复读演示的执行流程如下:

执行步骤客户端1(窗口1)客户端2(窗口2)说明
第 1 步
set session transaction isolation level read committed;
start transaction;
select * from userinfo;
设置事务隔离级别为读已提交;
开启事务;
查询用户列表,其中 Java 用户的余额是 100 元。
第 2 步start transaction;update userinfo set balance=balance+20 where name='Java';commit;
开启事务;
给 Java 用户的余额加 20 元;提交事务。
第 3 步
select * from userinfo;查询用户列表,其中 Java 用户的余额变成了 120 元。

窗口 2 同一个事务中的两次查询,得到了不同的结果这就是不可重复读,具体执行步骤如下。

不可重复读演示步骤1

设置窗口 2 的事务隔离级别为读已提交,设置命令如下:

set session transaction isolation level read committed;

PS:读已提交可以解决脏读的问题,但存在不可重复读的问题。

使用命令来检查当前连接窗口的事务隔离界别,如下图所示:ab92a7b483a68be5e4bb4f26600b29c0.png在窗口 2 中开启事务,并查询用户表,执行结果如下:b9e6582267480213267176ba846c745e.png此时查询的列表中,Java 用户的余额为 100 元。

不可重复读演示步骤2

在窗口 1 中开启事务,并给 Java 用户添加 20 元,但不提交事务,再观察窗口 2 中有没有脏读的问题,具体执行结果如下图所示:8d147dcb75c7596768d773c96d85119a.png从上述结果可以看出,当把窗口的事务隔离级别设置为读已提交,已经不存在脏读问题了。接下来在窗口 1 中提交事务,执行结果如下图所示:0ff9792e9b7fb70574fbfeabd6ba38ae.png

不可重复读演示步骤3

切换到窗口 2 中再次查询用户列表,执行结果如下:032bd0c30d928f0cf811986ce731f12e.png从上述结果可以看出,此时 Java 用户的余额已经变成 120 元了。在同一个事务中,先后查询的两次结果不一致就是不可重复读。

不可重复读和脏读的区别

脏读可以读到其他事务中未提交的数据,而不可重复读是读取到了其他事务已经提交的数据,但前后两次读取的结果不同。

幻读

幻读名如其文,它就像发生了某种幻觉一样,在一个事务中明明没有查到主键为 X 的数据,但主键为 X 的数据就是插入不进去,就像某种幻觉一样。幻读演示的执行流程如下:

执行步骤客户端1(窗口1)客户端2(窗口2)说明
第 1 步
set session transaction isolation level repeatable read;
start transaction;
select * from userinfo where id=3;
设置事务隔离级别为可重复读;
开启事务;
查询用户编号为 3 的数据,查询结果为空。
第 2 步start transaction;
insert into userinfo(id,name,balance) values(3,'Spring',100);
commit;

开启事务;
添加用户,用户编号为 3;
提交事务。
第 3 步
insert into userinfo(id,name,balance) values(3,'Spring',100);窗口 2 添加用户编号为 3 的数据,执行失败。
第 4 步
select * from userinfo where id=3;查询用户编号为 3 的数据,查询结果为空。

具体执行结果如下步骤所示。

幻读演示步骤1

设置窗口 2 为可重复读,可重复有幻读的问题,查询编号为 3 的用户,具体执行 SQL 如下:

set session transaction isolation level repeatable read;
start transaction;
select * from userinfo where id=3;

以上 SQL 执行结果如下图所示:e568bd80daeeb2db37088c8579575176.png从上述结果可以看出,查询的结果中 id=3 的数据为空。

幻读演示步骤2

开启窗口 1 的事务,插入用户编号为 3 的数据,然后成功提交事务,执行 SQL 如下:

start transaction;
insert into userinfo(id,name,balance) values(3,'Spring',100);
commit;

以上 SQL 执行结果如下图所示:7f279e847062698e12875d1797016e8e.png

幻读演示步骤3

在窗口 2 中插入用户编号为 3 的数据,执行 SQL 如下:

insert into userinfo(id,name,balance) values(3,'Spring',100);

以上 SQL 执行结果如下图所示:8d4e20b9512b05fdbc2ab42b38f254bf.png添加用户数据失败,提示表中已经存在了编号为 3 的数据,且此字段为主键,不能添加多个。

幻读演示步骤4

在窗口 2 中,重新执行查询:

select * from userinfo where id=3;

以上 SQL 执行结果如下图所示:bb898209e67854528d209bbbdc3568b9.png/ 在此事务中查询明明没有编号为 3 的用户,但插入的时候却却提示已经存在了,这就是幻读。

不可重复读和幻读的区别

二者描述的则重点不同,不可重复读描述的侧重点是修改操作,而幻读描述的侧重点是添加和删除操作。

总结

本文演示了 MySQL 的 4 种事务隔离级别:读未提交(有脏读问题)、读已提交(有不可重复读的问题)、可重复读(有幻读的问题)和序列化,其中可重复读是 MySQL 默认的事务隔离级别。脏读是读到了其他事务未提交的数据,而不可重复读是读到了其他事务已经提交的数据,但前后查询的结果不同,而幻读则是明明查询不到,但就是插入不了。

是非审之于己,毁誉听之于人,得失安之于数。 

公众号:Java面试真题解析

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

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

相关文章

保姆级教程,终于搞懂脏读、幻读和不可重复读了!(经典回顾)

作者 | 王磊来源 | Java中文社群(ID:javacn666)转载请联系授权(微信ID:GG_Stone)我的文章合集:https://gitee.com/mydb/interview在 MySQL 中事务的隔离级别有以下 4 种:读未提交&am…

WPF入门教程系列十五——WPF中的数据绑定(一)

使用Windows Presentation Foundation (WPF) 可以很方便的设计出强大的用户界面,同时 WPF提供了数据绑定功能。WPF的数据绑定跟Winform与ASP.NET中的数据绑定功能类似,但也有所不同,在 WPF中以通过后台代码绑定、前台XAML中进行绑定&#xff…

实战,实现幂等的8种方案!

前言 大家好,我是程序员田螺。今天我们一起来聊聊幂等设计。什么是幂等为什么需要幂等接口超时,如何处理呢?如何设计幂等?实现幂等的8种方案HTTP的幂等1. 什么是幂等? 幂等是一个数学与计算机科学概念。在数学中,幂等…

灰度共生矩阵及其数字特征_数字系统及其表示

灰度共生矩阵及其数字特征Any number system has a set of symbols known as Digits with some rules performing arithmetic operations. A collection of these makes a number has two parts. They are integer portion and fraction portion. These portions are separated…

绝绝子,画框架图就用这个工具

前言看过我以往文章的小伙伴可能会发现,我的大部分文章都有很多配图。我的文章风格是图文相结合,更便于大家理解。最近有很多小伙伴发私信问我:文章中的图是用什么工具画的。他们觉得我画的图风格挺小清新的,能够让人眼前一亮。先…

面试官:this和super有什么区别?this能调用到父类吗?

作者:磊哥来源 | Java面试真题解析(ID:aimianshi666)转载请联系授权(微信ID:GG_Stone)本文已收录《Java常见面试题》:https://gitee.com/mydb/interviewthis 和 super 都是 Java 中常…

SpringBoot 热部署神器快速重启的秘密!

今天咱们来聊聊这个热部署神器 spring-boot-devtools 的运行原理,看看它是怎么用这个 ClassLoader 来实现快速重启,帮我们节省时间的!😝文章概要文章的主旋律如下👇spring.factories我们直接打开 spring-boot-devtool…

计算机操作系统 内存_计算机内存的类型| 操作系统

计算机操作系统 内存什么是记忆? (What is Memory?) The essential component of the computer is its Memory. It is assembled on the motherboard as it is a storage device used for storing data and instructions for performing a task on the system. 计算…

关于 java 实现 语音朗读

最近有个java项目要实现 一个 java语音朗读的功能,百度了半天 没有现成的 。也是一头雾水。没具体思路。。。。。大体上总结了下网上的资料 1.java 实现起来 比c或者vb 能麻烦点,或者是这个功能用其他语言完成 然后整合到java 项目里面去!2.…

查询MySQL字段注释的 5 种方法!

作者 | 磊哥来源 | Java中文社群(ID:javacn666)转载请联系授权(微信ID:GG_Stone)很多场景下,我们需要查看 MySQL 中表注释,或者是某张表下所有字段的注释,所以本文就来盘…

聊聊索引失效的10种场景,太坑了

前言今天我接着上一期数据库的话题,更进一步聊聊索引的相关问题,因为索引是大家都比较关心的公共话题,确实有很多坑。不知道你在实际工作中,有没有遇到过下面的这两种情况:明明在某个字段上加了索引,但实际…

Java中的main方法

2019独角兽企业重金招聘Python工程师标准>>> 在一个Java应用程序中,通常程序的入口是一个main方法,它被声明为公有静态方法,参数是一个字符串数组,返回值为Void类型。这个方法有许多值得研究的地方,今天就来…

约瑟夫环问题(C++)

问题描述 首先,说明一下这个问题是研究生期间c课的综合作业,本来有好多选择但最后还是选择了约瑟夫环问题。下面是约瑟夫环的问题描述以及设计要求: 约瑟夫环(约瑟夫问题)是一个数学的应用问题:已知n个人&…

实战!工作中常用到哪些设计模式

前言 大家好,我是捡田螺的小男孩。平时我们写代码呢,多数情况都是流水线式写代码,基本就可以实现业务逻辑了。如何在写代码中找到乐趣呢,我觉得,最好的方式就是:使用设计模式优化自己的业务代码。今天跟大家…

什么是bcd码数据传输通讯_传输障碍| 数据通讯

什么是bcd码数据传输通讯传输障碍 (Transmission Impairment) In the data communication system, analog and digital signals go through the transmission medium. Transmission media are not ideal. There are some imperfections in transmission mediums. So, the signa…

Spring boot项目(问答网站)之timeline的推拉两种模式

Timeline介绍 所谓timeline就是当用户打开主页看到的随着时间轴发生的一系列时间的整合,主要包含: 关注用户的最新动态热门推荐广告推荐整合等等. 推、拉模式 推模式: 当一个用户关注了或者评论了一个问题或用户,触发事件&…

Bean放入Spring容器,你知道几种方式?

作者:三尺微命 一介书生来源:blog.csdn.net/weixin_43741092/article/details/120176466我们知道平时在开发中使用Spring的时候,都是将对象交由Spring去管理,那么将一个对象加入到Spring容器中,有哪些方式呢&#xff…

Spring boot项目(问答网站)之Python学习基础篇

简介 当问答网站基本框架搭建完毕之后需要一些初始的数据来进行填充,因此选用Python爬虫的方式,从网上截取一些资料信息(当然是自己做项目使用,非商用)放入到项目网站上面。这篇主要是关于Python基础知识的学习笔记。…

Spring Boot Admin,贼好使!

作者 | 磊哥来源 | Java中文社群(ID:javacn666)转载请联系授权(微信ID:GG_Stone)Spring Boot Admin(SBA)是一个开源的社区项目,用于管理和监控 Spring Boot 应用程序。应…

JAVA基础之容器基础内容

Java Collections框架 Java Collections框架中包含了大量的集合接口以及这些接口的实现类和操作它们的方法,具体包含了Set(集合)、List(列表)、Map(键值对)、Queue(队列)、Stack(栈)等,其中List、Set、Queue、Stack都继承了Collection接口。…