MySQL数据库中,在读已提交和可重复读这两个不同事务隔离级别下幻读的区别

目 录

    • 1. 前 言
      • 1.1 并发事务存在的问题
      • 1.2 事务的隔离级别
      • 1.3 快照读和当前读
    • 2. 不同事务隔离级别下幻读的区别
      • 2.1 读已提交下的幻读
      • 2.2 可重复读下的幻读
        • 2.2.1 情况一,无幻读
        • 2.2.2 情况二,有幻读
        • 2.2.3 情况三,有幻读
    • 3. 小 结

1. 前 言

在正式开始之前,先简单回顾一下并发事务存在的问题以及事务的隔离级别等内容。

1.1 并发事务存在的问题

当两个或者两个以上事务同时开启去处理同一个表的数据时,可能会存在以下的问题:

  • 丢失修改
  • 脏读
  • 不可重复读
  • 幻读

丢失修改

丢失修改是指当两个或多个事务更新同一行记录,产生更新丢失的现象,事务回滚覆盖和事务提交覆盖都会导致这种现象的产生。

脏读

一个事务能读取到另一个事务已经修改但还没有提交的数据。

不可重复读

在一个事务中多次执行同一条查询语句,读取到的数据内容前后不一致。

幻读

在一个事务中多次执行同一条查询语句,读取到的数据记录在数量上前后不一致,可能多了几条记录也可能少了几条记录。

1.2 事务的隔离级别

为了解决并发事务存在的问题,大佬们想到了一个手段,那就是对事务进行隔离,最好是做到各个事务各干各的,互不干涉,但理想很丰满,现实很骨感,哪可能一步到位呢。

为了应对不同的需要,解决不同的问题,于是决定将事务的隔离分成四个级别,分别是:

  • 读未提交
  • 读已提交
  • 可重复读
  • 串行化

它们各自能解决的并发事务问题如下表所示:

隔离级别 \ 事务问题事务回滚覆盖脏读不可重复读事务提交覆盖幻读
读未提交能解决不能解决不能解决不能解决不能解决
读已提交能解决能解决不能解决不能解决不能解决
可重复读能解决能解决能解决能解决不能完全解决,可能发生
串行化能解决能解决能解决能解决能解决

1.3 快照读和当前读

快照读

快照读是基于 MVCC 和 undo log 来实现的,读取数据的历史版本,得到一个 ReadView (事务视图) ,不对数据加锁,适用于简单 select 语句。

这里提一句,所谓 MVCC 并发版本控制,就是靠 ReadView (事务视图) 来实现的,多个 readView 组成 undo log(回滚日志)。

当前读

当前读是基于行锁 + 间隙锁来实现的,读取数据的最新版本,并对数据进行加锁,适用于 insert,update,delete, select ... for update, select ... lock in share mode 语句,以及加锁了的 select 语句

在更新数据时,都是先读后写,而这个读,就是指当前读,意味读取数据时,读到的是该条数据最新生成的 ReadView。

2. 不同事务隔离级别下幻读的区别

在上面的表格中,我们能看到,在读已提交这个隔离级别下,幻读是不能被解决的,也就是说会发生;而在可重复读这个隔离级别下,幻读则是没有完全被解决,只是解决了部分,意味着仍然有可能发生。

那它们分别是怎么产生的?各自又有什么表现?有何区别?下面我们举个例子来探究一下。

假设现在有两个事务,分别是事务 A 和 事务 B ,同时有一张学生表 student(表中只有一条记录,stu_name 为王大) ,我们用这两个事务来操作这张表。

2.1 读已提交下的幻读

在读已提交这种事务隔离级别下,两个事务的操作顺序如下:

事务A事务B
begin;
begin;
开始第一次查询:select * from student where stu_id > 0;
insert into student(stu_name) values(‘李二’);
commit;
开始第二次查询:select * from student where stu_id > 0;
  1. 事务 A 第一次查询,得到的数据记录是 stu_name 为王大这一条
  2. 事务 B 中途往表里插入了一条 stu_name 为李二的记录,并提交自身的事务
  3. 事务 A 用第一次查询的 SQL 语句进行第二次查询时,发现得到的数据记录成了两条,出现幻读

表现

普通的查询语句,能看到明显幻读现象。

原因剖析

事务 A 中用的是普通的 select 语句,因此采用的是快照读,但由于事务隔离级别为读已提交,在读已提交下,每次 select 操作,都会重新获取最新版本的数据,也正是因为这个原因,导致事务 A 中两次查询得到的结果在数量不一致,产生幻读。

解决方式

将事务隔离级别升级为可重复读

2.2 可重复读下的幻读

2.2.1 情况一,无幻读

在可重复读这种事务隔离级别下,两个事务的操作顺序如下:

事务A事务B
begin;
begin;
开始第一次查询:select * from student where stu_id > 0;
insert into student(stu_name) values(‘李二’);
commit;
开始第二次查询:select * from student where stu_id > 0;

像上面这样的事务操作,虽然在读已提交事务隔离级别下会产生幻读,但在可重复读事务隔离级别下,却不会产生幻读。

原因:在可重复读事务隔离级别下,针对普通的 select 语句,采用的是快照读,只会在第一次查询的时候获取一次数据的版本,往后继续做相同的 select 操作,不会重新获取,会延用前面得到的数据版本

细心的你有没有发现,在可重复读事务隔离级别下,对于快照读(普通的 select 操作),这不就是使用 MVCC 解决幻读问题吗?你没看错,是这样的。

2.2.2 情况二,有幻读

在可重复读这种事务隔离级别下,两个事务的操作顺序如下:

事务A事务B
begin;
begin;
开始第一次查询:select * from student where stu_id > 0;
insert into student(stu_name) values(‘李二’);
commit;
开始第二次查询:select * from student where stu_id > 0 for update;

上面的事务操作,和前面不同的地方在于,第二次查询加上了 for update ,也就是采用当前读。

表现

能看到明显的幻读现象

原因剖析

由于事务 A 第二次查询在 SQL 语句末尾加上了 for update ,表示采用当前读的方式,获取数据的最新版本,那么自然而然会把事务 B 中途插入的数据给查出来,从而出现幻读。

解决方式

用 next-key lock 解决,如果是走索引,会锁住索引本身的行锁;如果是范围,就会成为一个行锁+间隙锁,导致范围内的无法插入;如果是无索引的,直接全表加上了间隙锁,无法插入,阻塞。

具体的操作方式如下:

事务A事务B
begin;
begin;
开始第一次查询:select * from student where stu_id > 0 for update;
insert into student(stu_name) values(‘李二’);
commit;
开始第二次查询:select * from student where stu_id > 0 for update;

事务 A 的每次查询都加上 for update,这样就不会出现幻读,原因是事务 B 的 insert 操作会被阻塞,无法将数据插入到表中,从而避免幻读的出现。

2.2.3 情况三,有幻读

在可重复读这种事务隔离级别下,两个事务的操作顺序如下:

事务A事务B
begin;
begin;
开始第一次查询:select * from student where stu_id > 0;
insert into student(stu_name) values(‘李二’);
commit;
update student set stu_class = ‘03’ where stu_id is not null;
开始第二次查询:select * from student where stu_id > 0;

在上面的事务操作下,事务 B 能将数据记录正常插入到表中,而事务 A 做了一次 update 操作,从上面的介绍我们可以知道,update 操作是当前读,会获取到数据的最新版本,自然也能拿到事务 B 的提交记录。

表现

能明显看到幻读现象

原因剖析

事务 A 的 update 操作采用的是当前读,会获取数据的最新版本,将事务 B 提交的结果读取出来,后面事务 A 再做普通的 select 操作,采用快照读,由于延用 update 操作时得到的数据历史版本,因而产生幻读。

解决方式

同 2.2.2 中的解决方式一样,具体操作如下:

事务A事务B
begin;
begin;
开始第一次查询:select * from student where stu_id > 0 for update;
insert into student(stu_name) values(‘李二’);
commit;
update student set stu_class = ‘03’ where stu_id is not null;
开始第二次查询:select * from student where stu_id > 0 for update;

最简单的解决方式,就是再将事务隔离级别升级,改为串行化,毕竟串行化本身就解决所有事务问题,当然,这会牺牲效率。

3. 小 结

  1. 并发事务中,对于普通的 select 语句,在读已提交下,能明显看到幻读的现象,而在可重复读下,看不到幻读现象。
  2. 并发事务中,对于事务执行语句里含有当前读的情况,得具体问题具体分析,可能可以看到幻读,也可能由于加了 for update ,看不到幻读。

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

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

相关文章

aop切面调用失效问题排查

应用里有较多的地方访问外部供应商接口,由于公网网络不稳定或者外部接口不稳定(重启,发版,ip切换)的原因,经常报502或者504错误。为了解决HTTP调用的500报错,选择使用spring的retryable注解进行…

Pyinstaller将python文件打包成exe程序——封装LoFTR开源匹配代码

Pyinstaller将python文件打包成exe程序——封装LoFTR开源匹配代码 1.LoFTR代码下载及环境搭建 源码下载:https://github.com/bodhisatan/LoFTR-Stitch 环境搭建:按照github项目中的readme文档进行搭建即可,几乎没有遇到问题,代码…

【Unity入门】22.动态创建实例

【Unity入门】动态创建实例 大家好,我是Lampard~~ 欢迎来到Unity入门系列博客,所学知识来自B站阿发老师~感谢 (一)脚本实例化预制体对象 (1)Instantiate克隆创建对象 昨天我们学习了预制体这个概念&#…

文献阅读(50)—— Transformer 用于肺癌诊断预测

文献阅读(50)—— Transformer 用于肺癌诊断预测 文章目录 文献阅读(50)—— Transformer 用于肺癌诊断预测先验知识/知识拓展文章结构背景文章方法1. 文章核心网络结构2. Time Encoding ViT (TeViT)3. Tim…

力扣刷题2023-05-04-1——题目:2614. 对角线上的质数

题目: 给你一个下标从 0 开始的二维整数数组 nums 。 返回位于 nums 至少一条 对角线 上的最大 质数 。如果任一对角线上均不存在质数,返回 0 。 注意: 如果某个整数大于 1 ,且不存在除 1 和自身之外的正整数因子,…

Leetcode——66. 加一

💯💯欢迎来到的热爱编程的小K的Leetcode的刷题专栏 文章目录 1、题目2、暴力模拟(自己的第一想法)3、官方题解 1、题目 给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。最高位数字存放在数组的首位, 数组…

不同主题增删改查系统【控制台+MySQL】(Java课设)

有很多顾客都是只要实现各种各样的增删改查系统即可,只是主题和数据库表不一样,功能都是增删改查这四个功能,做出来的效果和下面的截图是一样的,后续这样的增删改查系统的运行效果请参考下面的截图,我就不一一演示了&a…

MATLAB实现工业PCB电路板缺陷识别和检测

PCB(PrintedCircuitBoard印刷电路板)是电子产品中众多电子元器件的承载体,它为各电子元器件的秩序连接提供了可能,PCB已成为现代电子产品的核心部分。随着现代电子工业迅猛发展,电子技术不断革新,PCB密集度…

【Git】‘git‘ 不是内部或外部命令,也不是可运行的程序

一、问题 我想利用git clone命令从github上下载项目源代码,发现报错: git 不是内部或外部命令,也不是可运行的程序或批处理文件。我用cmd跑一下git命令,发现报错: 二、问题分析 这个错误提示表明您的系统中没有安装…

电脑视频删除了怎么恢复回来?很着急

案例分享:“电脑视频删除了怎么恢复回来?我是一名影楼的摄像师,我的主要工作就是拍摄婚礼视频,最近拍了一场婚礼视频,当时由于相机的内存不足,于是将宣传片等视频都导入进了电脑里面,清空摄像机…

《软件工程教程》(第2版) 主编:吴迪 马宏茹 丁万宁 第八章课后习题参考答案

第八章 面向对象技术与UML 课后习题参考答案 一、单项选择题 D (2)C (3)B (4)D (5)C (6)B (7)A (8)C&…

2023华中杯数学建模C题完整模型代码

已完成全部模型代码,文末获取。 摘要 随着工业化和城市化的快速发展,空气污染已经成为全球性的环境问题。细颗粒物(PM2.5)等污染物对人类健康、生态环境和社会经济造成了严重影响。本研究旨在深入探究影响PM2.5浓度的主要因素&a…

ESP32(二):GPIO

一.创建例程 打开命令面板&#xff1a;ctrlshiftp&#xff0c;输入&#xff1a;esp-idf:example&#xff1b;选择hello_world工程&#xff0c;点击 Create project using example hello_world&#xff0c;选择保存工程&#xff1b;工具使用代码&#xff1a; #include <stdi…

【图像分割】视觉大模型SEEM(Segment Everything Everywhere All at Once)原理解读

文章目录 摘要&#xff08;效果&#xff09;二、前言三、相关工作四、method4.1 多用途4.2 组合性4.3 交互式。4.4 语义感知 五、实验 论文地址&#xff1a;https://arxiv.org/abs/2304.06718 测试代码&#xff1a;https://github.com/UX-Decoder/Segment-Everything-Everywher…

【Python】【进阶篇】14、Django创建第一个项目

目录 Django创建第一个项目1. 第一个项目BookStore1) BookStore项目创建 2. Django项目配置文件1) manage.py文件2) __init__.py文件3) settings.py文件4) urls.py文件5) wsgi.py文件 Django创建第一个项目 在上一章中&#xff0c;我们完成了开发环境的搭建工作。 本章我们将学…

NLP实战:基于Pytorch的文本分类入门实战

目录 一、前期准备 1.环境准备 2.加载数据 二、代码实战 1.构建词典 2.生成数据批次和迭代器 3. 定义模型 4. 定义实例 5.定义训练函数与评估函数 6.拆分数据集并运行模型 三、使用测试数据集评估模型 四、总结 这是一个使用PyTorch实现的简单文本分类实战案例。在…

【Java】内部类Object类

目录 1.内部类 1.1实例内部类 1.2静态内部类 1.3局部内部类 1.4匿名内部类 2.Object类 2.1getClass方法 2.2equals方法 2.3hashcode方法 1.内部类 定义&#xff1a;一个类定义在另一个类或一个方法的内部&#xff0c;前者称为内部类&#xff0c;后者称为外部类。 分…

spring常用的事务传播行为

事务传播行为介绍 Spring中的7个事务传播行为: 事务行为 说明 PROPAGATION_REQUIRED 支持当前事务&#xff0c;假设当前没有事务。就新建一个事务 PROPAGATION_SUPPORTS 支持当前事务&#xff0c;假设当前没有事务&#xff0c;就以非事务方式运行 PROPAGATION_MANDATORY…

Java——线程池详细讲解

文章目录 一、线程池一、线程池基础1.1 什么是线程池1.2 为什么使用线程池1.3 线程池有哪些优势1.4 应用场景 二、线程池使用2.1 Java内置线程池 ThreadPoolExecutor2.1.1 线程池的七个参数2.1.1.1 **int corePoolSize 核心线程数量**2.1.1.2 int maximumPoolSize 最大线程数2.…

[OtterCTF 2018]之Misc篇(NSSCTF)刷题记录⑦

NSSCTF-Misc篇-[OtterCTF 2018] [OtterCTF 2018]General Info[OtterCTF 2018]Play Time[OtterCTF 2018]Silly Rick[OtterCTF 2018]What the password?[OtterCTF 2018]Name Game[OtterCTF 2018]Hide And Seek[OtterCTF 2018]Name Game 2[OtterCTF 2018]Path To Glory[OtterCTF …
最新文章