Mysql的可重复读解决了幻读问题吗

news2024/11/15 5:01:03
  • 针对快照读(普通 select 语句),是通过 MVCC 方式解决了幻读,因为可重复读隔离级别下,事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的,即使中途有其他事务插入了一条数据,是查询不出来这条数据的,所以就很好了避免幻读问题。
  • 针对当前读(select ... for update 等语句),是通过 next-key lock(记录锁+间隙锁)方式解决了幻读,因为当执行 select ... for update 语句的时候,会加上 next-key lock,如果有其他事务在 next-key lock 锁范围内插入了一条记录,那么这个插入语句就会被阻塞,无法成功插入,所以就很好了避免幻读问题。

这两个解决方案是很大程度上解决了幻读现象,但是还是有个别的情况造成的幻读现象是无法解决的。

快照读是如何避免幻读的?

可重复读隔离级是由 MVCC(多版本并发控制)实现的,实现的方式是开始事务后(执行 begin 语句后),在执行第一个查询语句后,会创建一个 Read View,后续的查询语句利用这个 Read View,通过这个 Read View 就可以在 undo log 版本链找到事务开始时的数据,所以事务过程中每次查询的数据都是一样的,即使中途有其他事务插入了新纪录,是查询不出来这条数据的,所以就很好了避免幻读问题。

做个实验,数据库表 t_stu 如下,其中 id 为主键。

然后在可重复读隔离级别下,有两个事务的执行顺序如下:

从这个实验结果可以看到,即使事务 B 中途插入了一条记录,事务 A 前后两次查询的结果集都是一样的,并没有出现所谓的幻读现象。

#当前读是如何避免幻读的?

MySQL 里除了普通查询是快照读,其他都是当前读,比如 update、insert、delete,这些语句执行前都会查询最新版本的数据,然后再做进一步的操作。

这很好理解,假设你要 update 一个记录,另一个事务已经 delete 这条记录并且提交事务了,这样不是会产生冲突吗,所以 update 的时候肯定要知道最新的数据。

另外,select ... for update 这种查询语句是当前读,每次执行的时候都是读取最新的数据。

接下来,我们假设select ... for update当前读是不会加锁的(实际上是会加锁的),在做一遍实验。

这时候,事务 B 插入的记录,就会被事务 A 的第二条查询语句查询到(因为是当前读),这样就会出现前后两次查询的结果集合不一样,这就出现了幻读。

所以,Innodb 引擎为了解决「可重复读」隔离级别使用「当前读」而造成的幻读问题,就引出了间隙锁

假设,表中有一个范围 id 为(3,5)间隙锁,那么其他事务就无法插入 id = 4 这条记录了,这样就有效的防止幻读现象的发生。

举个具体例子,场景如下:

事务 A 执行了这面这条锁定读语句后,就在对表中的记录加上 id 范围为 (2, +∞] 的 next-key lock(next-key lock 是间隙锁+记录锁的组合)。

然后,事务 B 在执行插入语句的时候,判断到插入的位置被事务 A 加了 next-key lock,于是事物 B 会生成一个插入意向锁,同时进入等待状态,直到事务 A 提交了事务。这就避免了由于事务 B 插入新记录而导致事务 A 发生幻读的现象。

#幻读被完全解决了吗?

可重复读隔离级别下虽然很大程度上避免了幻读,但是还是没有能完全解决幻读

我举例一个可重复读隔离级别发生幻读现象的场景。

#第一个发生幻读现象的场景

还是以这张表作为例子:

事务 A 执行查询 id = 5 的记录,此时表中是没有该记录的,所以查询不出来。

# 事务 A
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from t_stu where id = 5;
Empty set (0.01 sec)

然后事务 B 插入一条 id = 5 的记录,并且提交了事务。

# 事务 B
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into t_stu values(5, '小美', 18);
Query OK, 1 row affected (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.00 sec)

此时,事务 A 更新 id = 5 这条记录,对没错,事务 A 看不到 id = 5 这条记录,但是他去更新了这条记录,这场景确实很违和,然后再次查询 id = 5 的记录,事务 A 就能看到事务 B 插入的纪录了,幻读就是发生在这种违和的场景

# 事务 A
mysql> update t_stu set name = '小林coding' where id = 5;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select * from t_stu where id = 5;
+----+--------------+------+
| id | name         | age  |
+----+--------------+------+
|  5 | 小林coding   |   18 |
+----+--------------+------+
1 row in set (0.00 sec)

整个发生幻读的时序图如下:

在可重复读隔离级别下,事务 A 第一次执行普通的 select 语句时生成了一个 ReadView,之后事务 B 向表中新插入了一条 id = 5 的记录并提交。接着,事务 A 对 id = 5 这条记录进行了更新操作,在这个时刻,这条新记录的 trx_id 隐藏列的值就变成了事务 A 的事务 id,之后事务 A 再使用普通 select 语句去查询这条记录时就可以看到这条记录了,于是就发生了幻读。

因为这种特殊现象的存在,所以我们认为 MySQL Innodb 中的 MVCC 并不能完全避免幻读现象

#第二个发生幻读现象的场景

除了上面这一种场景会发生幻读现象之外,还有下面这个场景也会发生幻读现象。

  • T1 时刻:事务 A 先执行「快照读语句」:select * from t_test where id > 100 得到了 3 条记录。
  • T2 时刻:事务 B 往插入一个 id= 200 的记录并提交;
  • T3 时刻:事务 A 再执行「当前读语句」 select * from t_test where id > 100 for update 就会得到 4 条记录,此时也发生了幻读现象。

要避免这类特殊场景下发生幻读的现象的话,就是尽量在开启事务之后,马上执行 select ... for update 这类当前读的语句,因为它会对记录加 next-key lock,从而避免其他事务插入一条新记录。

#总结

MySQL InnoDB 引擎的可重复读隔离级别(默认隔离级),根据不同的查询方式,分别提出了避免幻读的方案:

  • 针对快照读(普通 select 语句),是通过 MVCC 方式解决了幻读。
  • 针对当前读(select ... for update 等语句),是通过 next-key lock(记录锁+间隙锁)方式解决了幻读。

我举例了两个发生幻读场景的例子。

第一个例子:对于快照读, MVCC 并不能完全避免幻读现象。因为当事务 A 更新了一条事务 B 插入的记录,那么事务 A 前后两次查询的记录条目就不一样了,所以就发生幻读。

第二个例子:对于当前读,如果事务开启后,并没有执行当前读,而是先快照读,然后这期间如果其他事务插入了一条记录,那么事务后续使用当前读进行查询的时候,就会发现两次查询的记录条目就不一样了,所以就发生幻读。

所以,MySQL 可重复读隔离级别并没有彻底解决幻读,只是很大程度上避免了幻读现象的发生。

要避免这类特殊场景下发生幻读的现象的话,就是尽量在开启事务之后,马上执行 select ... for update 这类当前读的语句,因为它会对记录加 next-key lock,从而避免其他事务插入一条新记录。

For update:会对查询的所有结果进行加锁。

写倾斜:

这里再举两个例子:

这里两个事务都会成功,而且最后会导致没有医生值班

如何解决:再查询的时候可以使用for update对查询结果来进行加锁

这里如果使用for update来对第一个查询进行加锁,因为没有结果,所以for update不会对任何行进行加锁

mysql的临建锁:当 SQL 执行按照 非唯一索引 进行数据的检索时,会给匹配到行上加上临键锁。

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

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

相关文章

开关电源基础02:基本开关电源拓扑(3)-拓扑分析

说在开头:关于薛定谔的波动方程(1) 当年毛头小子海森堡在哥廷根求学的时候,埃尔文.薛定谔已经是瑞士苏黎世大学的著名教授了。跟其他那些小天才(定位精度:25岁5岁)相比,薛定谔只能用…

使用 Python 查找本月的最后一天

文章目录 使用 Python 中的日历库查找月份的最后一天使用 Python 中的 DateTime 模块查找该月的最后一天从每个月的第一天减去一天以找到该月的最后一天使用存储在数组中的预加载日期使用 for 循环查找该月的最后一天打印日历年所有月份的最后一天以查找该月的最后一天 使用 Ar…

Kafka生产者原理

消息发送流程介绍 Producer创建时,会创建⼀个sender线程并设置为守护线程。⽣产消息时,内部其实是异步的;⽣产的消息先经过拦截器->序列化器->分区器,然后将消息缓存在缓冲区(该缓冲区也是在Producer创建时创建…

关于clash退出后,华硕电脑连不上网了

关于clash退出后,华硕电脑连不上网了 问题记录 问题记录 昨天因为overleaf老断网,然后我就挂了一下clash(第一次用,不怎么懂),后来直接关闭退出了。 今天早上突然浏览器的网页(微软自带、华硕…

挑战14天学完Python----初识Python语法

往期文章 Java继承与组合 你知道为什么会划分数据类型吗?—JAVA数据类型与变量 10 > 20 && 10 / 0 0等于串联小灯泡?—JAVA运算符 你真的知道怎样用java敲出Hello World吗?—初识JAVA 目录 往期文章前言1.温度转换实例2. 程序格式框架2.1 高…

ATT汇编快速学习

说明 文档来源 https://flint.cs.yale.edu/cs421/papers/x86-asm/asm.html 使用AT&T语法; 原文档是intel语法翻译过来的; 内容 基于32位, x86的硬件环境; 指令仅仅介绍常用, 即还有很大一部分的指令并没有支持; 编译器(汇编器) GAS(GNU assembler: 即gnu组织提供; 使…

《斯坦福数据挖掘教程·第三版》读书笔记(英文版)Chapter 5 Link Analysis

来源:《斯坦福数据挖掘教程第三版》对应的公开英文书和PPT Chapter 5 Link Analysis Terms: words or other strings of characters other than white space. An inverted index is a data structure that makes it easy, given a term, to find (pointers to) a…

【数码】收音机,德生PL380使用教程与注意事项

文章目录 1、主界面功能介绍(注意闹钟和自动关机)2、电池和电池模式的匹配3、收音机天线与信号,耳机与噪音F、参考资料 1、主界面功能介绍(注意闹钟和自动关机) 红色的按钮:power 按一下开机,按…

DJYOS开源往事一:djyos爱好者大南山相聚写实

前言:DJYOS开源社区成立于2009年,后因为专注技术方向和垂直产业化等原因,2015年后关闭开源社区。斗转星移,DJYOS开源社区虽然关闭,但是DJYOS开源和精神依然在,转眼DJYOS发布十四年了。记录一下DJYOS开源往事…

Qt Plugin插件开发

一、Qt 插件机制 .1 Qt 插件简介 插件是一种遵循一定规范的应用程序接口编写出来的程序,定位于开发实现应用软件平台不具备的功能的程序。插件与宿主程序之间通过接口联系,就像硬件插卡一样,可以被随时删除,插入和修改&#xff…

2.2.4 Linux安装模式下,磁盘分区的选择(重要)

目录树结构 (directory tree) 整个Linux系统最重要的地方就是在于目录树架构。 所谓的目录树架构(directory tree)就是以根目录为主,然后向下呈现分支状的目录结构的一种文件架构。 所以,整个目录树架构最重…

Midjourney-Discord入门+高手指引手册

上一篇我们说了如何注册和订阅,今天我们来讲一讲相关的细节,首先我们来一个概览图,先有个大致的印象。 概览图 生成的四张图片,类似于Demo,从左至右,从上至下,1,2,3,4放大按钮U1,U2…

【旋转编码器如何工作以及如何将其与Arduino一起使用】

在本教程中,我们将学习旋转编码器的工作原理以及如何将其与Arduino一起使用。您可以观看以下视频或阅读下面的书面教程。 1. 概述 旋转编码器是一种位置传感器,用于确定旋转轴的角度位置。它根据旋转运动产生模拟或数字电信号。 有许多不同类型的旋转编码器按输出信号或传感…

三个令人惊艳超有用的 ChatGPT 项目,开源了!

自 3 月初 Open AI 开放 ChatGPT API 以来,GitHub 上诞生的开源项目数量之多,着实多得让我眼花缭乱、应接不暇。 今天,我将着重挑选几个对日常工作、生活、学习帮助较大的 ChatGPT 开源项目,跟大家分享下,希望对你有所…

Illustrator如何使用填充与线条之实例演示?

文章目录 0.引言1.绘制星空小插画2.制作清新小碎花壁纸3.双重描边文字 0.引言 因科研等多场景需要进行绘图处理,笔者对Illustrator进行了学习,本文通过《Illustrator CC2018基础与实战》及其配套素材结合网上相关资料进行学习笔记总结,本文对…

labelme标注数据集,并利用paddleseg完成标注数据的准备工作

1、安装labelme 1、创建labelme虚拟环境 先检查python的版本 python -V使用命令 conda create -n labelme python=3.9,创建虚拟环境 2、激活虚拟环境 conda activate labelme3、安装labelme pip install labelme2、labelme的使用 1、打开cmd输入命令 labelme2、进入label…

关于安装Node/Yarn/Electron过程中遇到的问题

目录 1、安装Node2、安装electron很慢3、PowerShell中无法使用yarn命令4、Yarn命令目录bin与其全局安装位置不在同一个文件夹 1、安装Node 【参考文章】Node.js下载安装及环境配置教程 2、安装electron很慢 npm config set electron_mirror https://npm.taobao.org/mirrors/…

Java日志详解

文章目录 1.日志的概述1.1 日志文件1.1.1 调试日志1.1.2 系统日志 1.2 JAVA日志框架1.2.1 为什么要用日志框架1.2.2 日志框架和日志门面 2.JUL2.1 JUL简介2.2 JUL组件介绍2.3 JUL的基本使用2.3.1 日志输出的级别2.3.2 日志的输出方式2.3.3 自定义日志的级别2.3.4 将日志输出到具…

20个最流行的3D打印机切片软件

3D 打印切片机(Slicer)通过生成 G 代码为你的 3D 打印机准备 3D 模型,G 代码是一种广泛使用的数控 (NC) 编程语言。 3D打印切片软件的选择范围很广。 因此,为了帮助你找到最合适的工具,本文列出了20个顶级 3D 打印切片…

2022年NOC大赛编程马拉松赛道决赛图形化高年级A卷-正式卷

2022年NOC大赛编程马拉松赛道决赛图形化高年级A卷-正式卷 2022NOC-图形化决赛高年级A卷正式卷 编程题:蓝色星球的绿色踪迹任务描述:有一颗蔚蓝色的星球,她就是我们人类赖以生存的家园——地球。地球是人类的母亲,她为我们提供了各…