MySQL是否解决幻读问题

MySQL是否解决幻读问题

MySQL事务隔离级别

✓ 读未提交(Read Uncommitted):最低的隔离级别,会读取到其他事务还未提交的内容,存在脏读。

✓ 读已提交(Read Committed):读取到的内容都是已经提交的,可以解决脏读,但是存在不可重复读。

✓ 可重复读(Repeatable Read):在一个事务中多次读取时看到相同的内容,可以解决不可重复读,但是存在幻读。但是在 InnoDB 中不存在幻读问题,对于快照读,InnoDB 使用 MVCC 解决幻读,对于当前读,InnoDB 通过 gap locks 或 next-key locks 解决幻读。

✓ 串行化(Serializable):最高的隔离级别,串行的执行事务,没有并发事务问题。

MySQL 默认的事务隔离级别是可重复读(REPEATABLE READ)

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

事务隔离级别

脏读

不可重复读

幻读

读未提交(READ UNCOMMITTED)

读已提交(READ COMMITTED)

×

可重复读(REPEATABLE READ)

×

×

串行化(SERIALIZABLE)

×

×

×

不可重复度和幻读的区别

不可重复读 主要是说多次读取一条记录, 发现该记录中某些列值被修改过。

幻读 主要是说多次读取一个范围内的记录(包括直接查询所有记录结果或者做聚合统计), 发现结果不一致(标准档案一般指记录增多, 记录的减少应该也算是幻读)。(可以参考)

模拟幻读问题

环境准备

事务相关的命令

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

MySQL8查询事务隔离级别的命令

select @@global.transaction_isolation,@@transaction_isolation;

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

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

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

测试数据准备

创建测试数据库和表信息,执行 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);

创建的表结构和数据如下:

mysql> select * from userinfo;
+----+-------+---------+
| id | name  | balance |
+----+-------+---------+
|  1 | Java  | 100.00  |
|  2 | MySQL | 200.00  |
+----+-------+---------+
2 rows in set (0.00 sec)

幻读模拟

接下来会使用两个窗口(两个客户端)来演示事务在可重复读隔离级别下的幻读的问题。

幻读场景1

在一个事务中明明没有查到主键为 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 的数据,查询结果为空。

在窗口2中查询id为3的记录为空

mysql> set session transaction isolation level repeatable read;
Query OK, 0 rows affected (0.00 sec)
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from userinfo where id=3;
Empty set

在窗口1中插入一条id为3的记录

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into userinfo(id,name,balance) values(3,'Spring',100);
Query OK, 1 row affected (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.00 sec)

在窗口2中插入id为3的记录

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

发现插入失败

mysql> insert into userinfo(id,name,balance) values(3,'Spring',100);
1062 - Duplicate entry '3' for key 'userinfo.PRIMARY'

继续在窗口2查询id为3的记录,发现查询不到

mysql> select * from userinfo where id=3;
Empty set

幻读场景2

在一个事务A中一开始查询不到id为X的记录,但在另一个事务B中插入id为X的记录并提交事务后,在事务A中再更新id为X的记录可以更新到,再次查询id为X的记录也可以查询到。

执行步骤

客户端1(窗口1)

客户端2(窗口2)

说明

第 1 步

set session transaction isolation

 level repeatable read;

start transaction;

select * from userinfo where id=4;

设置事务隔离级别为可重复读;开启事务;查询用户编号为 4 的数据,查询结果为空。

第 2 步

set session transaction isolation

 level repeatable read;

start transaction;

select * from userinfo where id=4;

insert into userinfo(id,name,balance)

 values(4,'Redis',100);

commit;

开启事务;添加用户,用户编号为 4;提交事务。

第 3 步

select * from userinfo where id=4;

查询用户编号为 3 的数据,查询结果为空。此时还未出现幻读问题

第 4 步

update userinfo set name='kafka' 

where id = 4;

更新用户编号为4的记录,更新成功

第 5 步

select * from userinfo where id=4;

查询到用户编号为4的记录,查询到数据,出现幻读问题

第一步,在窗口1中查询id为4的记录,查询结果为空

mysql> set session transaction isolation level repeatable read;Query OK, 0 rows affected (0.00 sec)mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)mysql> select * from userinfo where id=4;Empty set

第二步,在窗口2中查询id为4的记录,查询结果为空。

插入一条id为4 的记录并提交事务

mysql> set session transaction isolation level repeatable read;Query OK, 0 rows affected (0.00 sec)mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)mysql> select * from userinfo where id=4;Empty setmysql> insert into userinfo(id,name,balance) values(4,'Redis',100);Query OK, 1 row affected (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.00 sec)mysql> 

第三步,在窗口1中查询id为4 的记录,查询结果为空,此时还没有幻读问题

mysql> select * from userinfo where id=4;Empty set

第四步,在窗口1中更新id为4 的记录,更新成功

mysql> update userinfo set name='kafka' where id = 4;Query OK, 1 row affected (0.00 sec)
Rows matched1  Changed: 1  Warnings0

第五步,在窗口1中查询id为4 的记录,查询到更新后的数据,出现幻读问题

mysql> select * from userinfo where id=4;+----+-------+---------+
| id | name  | balance |
+----+-------+---------+
|  4 | kafka | 100.00  |
+----+-------+---------+
1 row in set (0.00 sec)

幻读场景3

在事务A中查询id为X的记录查询不到,在事务B中插入一条id为X的记录后,再到事务A中对表执行当前读 for update可以查询到id为X的记录。

执行步骤

客户端1(窗口1)

客户端2(窗口2)

说明

第 1 步

set session transaction isolation
 level repeatable read;

start transaction;

select * from userinfo;

设置事务隔离级别为可重复读;开启事务;查询用户表所有数据,查询结果为4条。

第 2 步

set session transaction isolation

 level repeatable read;

start transaction;

select * from userinfo where id=5;

insert into userinfo(id,name,balance) 

values(5,'Pulsar',100);

commit;

开启事务;添加用户,用户编号为 5;提交事务。

第 3 步

select * from userinfo for update;

执行当前读for update,查询出了id为5的记录,出现幻读问题。

第一步,在窗口1中查询用户表的所有记录,查询结果为4条

mysql> set session transaction isolation level repeatable read;Query OK, 0 rows affected (0.00 sec)mysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> select * from userinfo;+----+--------+---------+
| id | name   | balance |
+----+--------+---------+
|  1 | Java   | 100.00  |
|  2 | MySQL  | 200.00  |
|  3 | Spring | 100.00  |
|  4 | kafka  | 100.00  |
+----+--------+---------+
4 rows in set (0.00 sec)

第二步,在窗口2中查询id为5的记录,查询结果为空。

插入一条id为5 的记录并提交事务

mysql> set session transaction isolation level repeatable read;Query OK, 0 rows affected (0.00 sec)mysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> select * from userinfo where id=5;Empty setmysql> insert into userinfo(id,name,balance) values(5,'Pulsar',100);Query OK, 1 row affected (0.00 sec)
mysql> commit;Query OK, 0 rows affected (0.00 sec)

第三步,窗口1执行当前读for update,查询出了id为5的记录,出现了幻读问题。

mysql> select * from userinfo for update;+----+--------+---------+
| id | name   | balance |
+----+--------+---------+
|  1 | Java   | 100.00  |
|  2 | MySQL  | 200.00  |
|  3 | Spring | 100.00  |
|  4 | kafka  | 100.00  |
|  5 | Pulsar | 100.00  |
+----+--------+---------+
5 rows in set (0.00 sec)

结论

不可重复读的重点在于 update 和 delete, 而幻读着重强调 insert 操作, 当用户第一次读取时,存在结果集 a, b, c,第二次读取时结果集为 a, b, c, d 即多出来的一条数据 d 表示发送幻读现象。

不可重复读的现象是用户第一次读取,返回结果集 a, 而第二次读取返回结果 a’, 即数据发生了变更. 而 a’ 数据确实由一个已经 committed 的事务修改. 不可重复读现象需要隔离级别为 Repeatable Read 来规避. 在 InnoDB 中使用 MVCC 机制实现。

通常来说, MVCC 的多版本控制并不能保证在 RR 隔离级别下完全避免幻读, 但 InnoDB 通过 MVCC + Next key lock 的方式来保证在 RR 隔离级别下避免部分场景下幻读。

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

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

相关文章

chatglm微调

chatGML 看到 【【官方教程】ChatGLM-6B 微调:P-Tuning,LoRA,Full parameter】 【精准空降到 15:27】 https://www.bilibili.com/video/BV1fd4y1Z7Y5/?share_sourcecopy_web&vd_sourceaa8c13cff97f0454ee41e1f609a655f1&t927 记得看…

npm 加速 国内镜像源

一、修改成腾讯云镜像源 1、命令 npm config set registry http://mirrors.cloud.tencent.com/npm/ 验证命令 npm config get registry 如果返回http://mirrors.cloud.tencent.com/npm/,说明镜像配置成功。 二、修改成淘宝镜像源 命令 npm config set regist…

一文了解Docker之网络模型

目录 1.Docker网络 1.1 Docker网络模型概述 1.2 Docker网络驱动程序 1.2.1 host模式 1.2.2 bridge模式 1.2.3 container模式 1.2.4 none模式 1.3 Docker网络命令示例 1.3.1 创建一个自定义网络 1.3.2 列出所有网络 1.3.3 连接容器到网络 1.3.4 断开容器与网络的连接…

SpringCloud(三)LoadBalancer负载均衡

一、负载均衡 实际上,在添加LoadBalanced注解之后,会启用拦截器对我们发起的服务调用请求进行拦截(注意这里是针对我们发起的请求进行拦截),叫做LoadBalancerInterceptor,它实现ClientHttpRequestIntercep…

Android 系统的分区介绍

由于Android系统采用Linux架构,所以Android的系统分区可以类比同样采用Linux架构的操作系统(如Windows)。 Android系统分区分类 现在一般常见的Android分区方式共有三种,在不同的Android系统版本上会采用不同的分区方式。 1、传…

Postman接口自动化之postman脚本编写

这是之前搞的接口自动化方案,已经在业务测试中实现了使用postman编写接口脚本,通过GitHubJenkinsemail html report实现了接口自动化,现在分块整理一下。 postman脚本编写 1、创建集合 和 目录: 一条业务线下的接口可以放到一个…

Android adb说明与详解

Android adb 说明与详解 Android Debug Bridge(ADB)是一个非常有用的工具,它可以帮助开发人员在Android设备和计算机之间进行通信,以便在设备上进行调试、测试和安装应用程序。 1. 安装ADB 在使用ADB之前,您需要安装…

sonarqube安装并配置CI/CD

sonarqube安装使用 目录 简介效果(配置在下面查看)官方文档环境准备配置compose-sonarqube.yml启动登录集成Gitlab 获取私钥sonarqube配置gitlab查看项目 配置 手动方式Gitlab CI/CD 自动检测 简介 SonarQube是一个开源的代码质量管理平台,用于对代码进行静态代…

【数学建模】——相关系数

第一部分:皮尔逊相关系数的计算以及数据的描述性统计 本讲我们将介绍两种最为常见的相关系数:皮尔逊person相关系数和斯皮尔曼spearman等级相关系数。它们可以用来衡量两个变量之间的相关性的大小,根据数组满足的不同条件,我们要选…

Monocular 3D Object Detection with Depth from Motion 论文学习

论文链接:Monocular 3D Object Detection with Depth from Motion 1. 解决了什么问题? 从单目输入感知 3D 目标对于自动驾驶非常重要,因为单目 3D 的成本要比多传感器的方案低许多。但单目方法很难取得令人满意的效果,因为单张图…

QT之智能指针

如果没有智能指针,程序员必须保证new对象能在正确的时机delete,四处编写异常捕获代码以释放资源,而智能指针则可以在退出作用域时(不管是正常流程离开或是因异常离开)总调用delete来析构在堆上动态分配的对象。 来看看一个野指针例子 程序将会…

封装React组件DragLine,鼠标拖拽的边框改变元素宽度

原文合集地址如下,有需要的朋友可以关注 本文地址 合集地址 在项目中,设计说想做个面板,其宽度随鼠标拖拽而变化,有最大最小值。基于这个小功能封装一个可拖拽组件,在需要的地方引入即可。 思路 这里只是实现x方向…

java学习路程之篇三、知识点、类、模块、项目、操作、下载、安装、IDEA

文章目录 1、IDEA开发工具2、IDEA的下载和安装3、IDEA中的第一个代码4、IDEAZ中的类、模块、项目的操作 1、IDEA开发工具 2、IDEA的下载和安装 3、IDEA中的第一个代码 4、IDEAZ中的类、模块、项目的操作

创建型设计模式-2.工厂设计模式

创建型设计模式-2.工厂设计模式 一、示例代码 下面是一个没有使用工厂设计模式的例子,ResourceLoader 类负责根据 URL 的前缀来加载资源。根据不同的前缀,它执行不同的操作来创建 Resource 对象。这导致了以下问题: 代码冗余:在…

【计算机视觉 | 图像分类】arxiv 计算机视觉关于图像分类的学术速递(7 月 17 日论文合集)

文章目录 一、分类|识别相关(11篇)1.1 Multimodal Distillation for Egocentric Action Recognition1.2 Dual-Query Multiple Instance Learning for Dynamic Meta-Embedding based Tumor Classification1.3 Interactive Spatiotemporal Token Attention Network for Skeleton-…

【面试官】说说你对闭包的理解

说说你对闭包的理解 闭包是什么闭包的三个特性优缺点应用场景javascript的垃圾回收原理:通常情况下有两种实现方式垃圾回收原理的缺陷GC优化策略 闭包是什么 闭包就是能够读取其他函数内部变量的函数闭包是指有权访问另一个函数作用域中变量的函数,创建闭包的最常见…

基于ClickHouse解决活动海量数据问题 | 京东云技术团队

1、背景 魔笛活动平台要记录每个活动的用户行为数据,帮助客服、运营、产品、研发等快速处理客诉、解决线上问题并进行相关数据分析和报警。可以预见到需要存储和分析海量数据,预估至少几十亿甚至上百亿的数据量,所以需要选择一款能存储海量数…

会议音响系统麦克风阵列波束形成算法C语言实现

一 应用麦克风阵列波束成形算法做的项目产品 二 麦克风波束形成技术应用领域? 麦克风波束形成技术是一种利用多个麦克风阵列来实现声音定向捕捉和增强的技术。通过对多个麦克风信号进行处理和合成,可以使麦克风系统在特定方向上具有更高的灵敏度和抑制非期望方向上的噪…

机器学习(13)--支持向量机

目录 一、支持向量机概述 二、Sklearn中的SVM概述 三、线性SVM损失函数 四、sklearn中进行可视化 1、导入模块 2、实例化数据集,可视化 3、网格点制作 4、建立模型并绘制决策边 目录 一、支持向量机概述 二、Sklearn中的SVM概述 三、线性SVM损失函数 四…

机器学习技术(三)——机器学习实践案例总体流程

机器学习实践案例总体流程 文章目录 机器学习实践案例总体流程一、引言二、案例1、决策树对鸢尾花分类1.数据来源2.数据导入及描述3.数据划分与特征处理4.建模预测 2、各类回归波士顿房价预测1.案例数据2.导入所需的包和数据集3.载入数据集,查看数据属性&#xff0c…