MySQL45讲 第二十讲 幻读是什么,幻读有什么问题?
文章目录
- MySQL45讲 第二十讲 幻读是什么,幻读有什么问题?
- 一、幻读的定义
- 二、幻读带来的问题
- (一)语义问题
- (二)数据一致性问题
- 三、InnoDB 解决幻读的方法
- 四、总结
MySQL45讲 第二十讲 幻读是什么,幻读有什么问题?
在数据库事务处理的复杂世界里,幻读是一个不容忽视的重要概念。它不仅关乎数据的一致性,还与事务隔离性紧密相连。今天,我们就一同深入探讨幻读的奥秘,解析其定义、所引发的问题,以及 InnoDB 是如何巧妙解决这一难题的。
一、幻读的定义
幻读究竟是什么呢?简单来说,在可重复读隔离级别下,当一个事务对同一个范围进行前后两次查询时,后一次查询竟然发现了前一次查询中未曾出现的行。这就好比在一个神秘的魔法世界里,数据会 “凭空” 出现或消失,让人捉摸不透。
假设有一个名为t的表,其结构如下:
CREATE TABLE `t` (
`id` int(11) NOT NULL,
`c` int(11) DEFAULT NULL,
`d` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `c` (`c`)
) ENGINE=InnoDB;
insert into t values(0,0,0),(5,5,5),
(10,10,10),(15,15,15),(20,20,20),(25,25,25);
在这个表中,除了主键id外,还有一个索引c,并且已经初始化插入了 6 行数据。
现在,我们考虑这样一个事务操作序列。在事务 A 中,执行了三次查询语句select * from t where d = 5 for update,分别标记为 Q1、Q2 和 Q3。根据事务可见性规则,这些查询使用了当前读,应该能够读到所有已提交记录的最新值。

- Q1 查询时,表中只有
id = 5这一行满足d = 5的条件,所以 Q1 只返回了这一行。 - 在 T2 时刻,事务 B 执行了
update t set d = 5 where id = 0,将id = 0这一行的d值修改为 5。此时,事务 A 的 Q2 查询就会看到id = 0和id = 5这两行,因为事务 B 的修改已经提交,Q2 需要读到最新值。 - 接着,在 T4 时刻,事务 C 插入了一行
(1,1,5)。当事务 A 执行 Q3 查询时,就会看到id = 0、id = 1和id = 5这三行。
这里,Q3 读到id = 1这一行的现象就是幻读。需要注意的是,在可重复读隔离级别下,普通查询是快照读,不会看到其他事务插入的数据,幻读仅在当前读下才会出现。而且,幻读特指新插入的行,像事务 B 的修改结果被事务 A 之后的查询用当前读看到,并不属于幻读范畴。
二、幻读带来的问题
(一)语义问题
从语义角度来看,幻读会导致事务的加锁声明失去意义。
就像事务 A 在 T1 时刻声明要锁住所有d = 5的行,禁止其他事务进行读写操作。然而,由于幻读的存在,事务 B 可以修改id = 0这一行的d值为 5,事务 C 还能插入新的(1,1,5)行,这显然破坏了事务 A 的加锁语义。
我们通过一个详细的场景来进一步说明。假设事务 B 和事务 C 在执行修改和插入操作时,还分别执行了其他相关操作:
| session A | session B | session C | |
|---|---|---|---|
| T1 | begin; select * from t where d = 5 for update; / * Q1 * / | ||
| T2 | update t set d = 5 where id = 0; update t set c = 5 where id = 0; | ||
| T3 | select * from t where d = 5 for update; / * Q2 * / | ||
| T4 | insert into t values(1,1,5); update t set c = 5 where id = 1; | ||
| T5 | select * from t where d = 5 for update; / * Q3 * / | ||
| T6 | commit; |
事务 B 的第二条语句update t set c = 5 where id = 0,语义是修改id = 0、d = 5这一行的c值为 5。但由于事务 A 在 T1 时刻只给id = 5这一行加了行锁,没有锁住id = 0这行,所以事务 B 在 T2 时刻可以执行这两条更新语句,这就与事务 A 中 Q1 语句要锁住所有d = 5的行的语义相违背。同样,事务 C 对id = 1这一行的修改也破坏了 Q1 的加锁声明。
(二)数据一致性问题
幻读还会引发数据一致性问题,这涉及到数据库内部数据状态以及数据和日志在逻辑上的一致性。
我们在事务 A 的 T1 时刻添加一个更新语句update t set d = 100 where d = 5,然后分析整个执行序列完成后的情况。
经过 T1 时刻,id = 5这一行变成(5,5,100),最终在 T6 时刻提交。T2 时刻,id = 0这一行变为(0,5,5);T4 时刻,表中新增了一行(1,5,5)。此时,数据库中的数据看起来似乎没有问题。
但是,当我们查看 binlog 中的内容时,就会发现问题。T2 时刻,事务 B 提交,写入了两条语句;T4 时刻,事务 C 提交,写入了两条语句;T6 时刻,事务 A 提交,写入了update t set d = 100 where d = 5这条语句。按照 binlog 的执行顺序,最终id = 0和id = 1这两行的结果会变成(0,5,100)和(1,5,100),与数据库中的实际结果不一致。
这种数据不一致的情况非常严重,它可能导致数据的错乱,影响系统的正常运行。例如,在一个电商系统中,如果出现这种数据不一致,可能会导致订单信息错误、库存数量不准确等问题,给企业带来巨大的损失。
三、InnoDB 解决幻读的方法
为了解决幻读问题,InnoDB 引入了一种新的锁机制 —— 间隙锁(Gap Lock)。间隙锁,顾名思义,就是锁住两个值之间的空隙。
以我们之前的表t为例,初始化插入 6 个记录后,会产生 7 个间隙,分别是(-∞,0)、(0,5)、(5,10)、(10,15)、(15,20)、(20,25)、(25,+∞)。当执行select * from t where d = 5 for update时,InnoDB 不仅会给已有的 6 个记录加上行锁,还会同时给这 7 个间隙加上间隙锁,确保无法再插入新的记录。
间隙锁和行锁合称 Next - Key Lock,每个 Next - Key Lock 是前开后闭区间。例如,当使用select * from t for update要锁住整个表所有记录时,就会形成 7 个 Next - Key Lock,分别是(-∞,0]、(0,5]、(5,10]、(10,15]、(15,20]、(20, 25]、(25, +supremum]。这里的+supremum是 InnoDB 为每个索引添加的一个不存在的最大值,用于满足前开后闭区间的定义。
间隙锁的引入虽然解决了幻读问题,但也带来了一些新的困扰。由于间隙锁会锁定更大的范围,可能会导致并发度下降,甚至引发死锁。
例如,考虑这样一个业务逻辑:任意锁住一行,如果该行不存在则插入,如果存在则更新其数据。

在并发情况下,可能会出现死锁现象。假设两个事务 A 和 B 都试图执行这个逻辑,且都要操作id = 9这一行(假设该行初始不存在)
- 事务 A 先执行
select * from t where id = 9 for update,由于id = 9不存在,会加上间隙锁(5,10)。 - 接着事务 B 也执行
select * from t where id = 9 for update,同样加上间隙锁(5,10),此时间隙锁之间不冲突,事务 B 的语句可以执行成功。 - 然后事务 B 试图插入一行
(9,9,9),但被事务 A 的间隙锁挡住,进入等待状态。 - 而事务 A 此时也试图插入
(9,9,9),同样被事务 B 的间隙锁挡住,两个事务就进入了互相等待的死锁状态。
当然,InnoDB 的死锁检测机制会及时发现这种死锁关系,并让其中一个事务的插入语句报错返回,以避免系统长时间阻塞。
如果想要避免间隙锁带来的这些问题,还有一种配置选择,就是将隔离级别设置为读提交。在这种隔离级别下,就没有间隙锁了,但需要将 binlog 格式设置为 row,以解决可能出现的数据和日志不一致问题。不过,这种配置是否合理,需要根据具体的业务场景来分析。如果业务不需要可重复读的保证,读提交隔离级别下操作数据的锁范围更小,可能是一个合理的选择。但如果盲目跟风使用这种配置,而没有考虑业务实际需求,可能会在后续的运行中出现各种问题。
四、总结
-
幻读在数据库事务处理中是一个复杂而关键的问题,它对数据的一致性和事务的隔离性有着重要影响。通过本文的详细分析,我们了解到幻读的定义、产生的问题以及 InnoDB 的解决方案。
-
在实际应用中,我们需要深入理解这些概念,根据业务需求合理选择事务隔离级别和配置。如果对间隙锁等机制理解不足,可能会导致生产库上出现死锁等问题,影响系统的性能和稳定性。希望本文能够帮助读者更好地掌握幻读相关知识,在数据库设计和开发中做出明智的决策,构建出更加可靠、高效的数据库应用系统。
相关文章:
MySQL45讲 第二十讲 幻读是什么,幻读有什么问题?
文章目录 MySQL45讲 第二十讲 幻读是什么,幻读有什么问题?一、幻读的定义二、幻读带来的问题(一)语义问题(二)数据一致性问题 三、InnoDB 解决幻读的方法四、总结 MySQL45讲 第二十讲 幻读是什么࿰…...
MySQL技巧之跨服务器数据查询:进阶篇-从A数据库复制到B数据库的表中
MySQL技巧之跨服务器数据查询:进阶篇-从A数据库复制到B数据库的表中 基础篇已经描述:借用微软的SQL Server ODBC 即可实现MySQL跨服务器间的数据查询。 而且还介绍了如何获得一个在MS SQL Server 可以连接指定实例的MySQL数据库的连接名: MY_ODBC_MYSQ…...
【论文阅读】利用SEM二维图像表征黏土矿物三维结构
导言 在油气储层研究中,黏土矿物对流体流动的影响需要在微观尺度上理解,但传统的二维SEM图像难以完整地表征三维孔隙结构。常规的三维成像技术如FIB-SEM(聚焦离子束扫描电子显微镜)虽然可以获取高精度的3D图像,但成本…...
可靠UDP协议(KCP)使用说明
希望这篇文章,对学习和使用 KCP 协议的读者,有帮助。 1. KCPUDP 流程图 2. 示例代码(待补充) #include <iostream>int main() {// TODO: kcp examplereturn 0; }...
ffmpeg+D3D实现的MFC音视频播放器,支持录像、截图、音视频播放、码流信息显示等功能
一、简介 本播放器是在vs2019下开发,通过ffmpeg实现拉流解码功能,通过D3D实现视频的渲染功能。截图功能采用libjpeg实现,可以截取jpg图片,图片的默认保存路径是在C:\MYRecPath中。录像功能采用封装好的类Mp4Record实现,…...
【Flink】-- flink新版本发布:v2.0-preview1
目录 1、简介 2、非兼容变更 2.1、API 2.2、连接器适配计划 2.3、配置 2.4、其它 3、重要新特性 3.1、存算分离状态管理 3.2、物化表 3.3、批作业的自适应执行 3.4、流式湖仓 4、附加 4.1、非兼容性的 api 程序变更 4.1.2、Removed Classes # 4.1.3、Modified Cl…...
Node.js 版本管理的最终答案 Volta
文章目录 特点安装Unix系统安装Windows系统安装 常用命令volta fetchvolta installvolta uninstallvolta pinvolta listvolta completionsvolta whichvolta setupvolta runvolta help 建议 目前对于前端项目的node 版本,我们一般会在项目 package.json 的 engines 字…...
蓝桥杯每日真题 - 第11天
题目:(合并数列) 题目描述(14届 C&C B组D题) 解题思路: 题意理解:给定两个数组,目标是通过若干次合并操作使两个数组相同。每次合并操作可以将数组中相邻的两个数相加ÿ…...
Vue vs React:两大前端框架的区别解析
在现代前端开发中,Vue.js 和 React.js 是两个最受欢迎的框架和库。我们常常面临选择它们的困惑。虽然这两者在本质上都是为了构建用户界面而设计的,但它们在设计理念、使用方式和生态系统等方面有着显著的区别。今天,我们将通过深入分析这两个…...
【树莓派raspberrypi烧录Ubuntu远程桌面登入树莓派】
提示:本文利用的是Ubuntu主机和树莓派4B开发板,示例仅供参考 文章目录 一、树莓派系统安装下载前准备工作下载安装树莓派的官方烧录软件imagerimager的使用方法 二、主机与树莓SSH连接查看数梅派IP地址建立ssh连接更新树莓派源地址 三、主机端远程桌面配…...
c# 调用c++ 的dll 出现找不到函数入口点
今天在调用一个设备的dll文件时遇到了一点波折,因为多c 不熟悉,调用过程张出现了找不到函数入口点,一般我们使用c# 调用c 文件,还是比较简单。 [DllImport("AtnDll2.dll",CharSet CharSet.Ansi)]public static extern …...
LInux——环境基础开发工具使用(正在更新中...)
1.软件包管理器 Linux下安装软件的方案: 1. 源代码安装 2. rpm包安装 3. 包管理器安装 --- yum/ apt (此图片来自于比特就业课课件) 1.1 操作生态系统 好的操作系统定义: 生态环境好 不同的操作系统根本是生态不同(…...
linux 内核asmlinkage关键字总结
1,看一下asmlinkage的定义 CPP_ASMLINKAGE __attribute__((regparm(0))) GCC中使用__attribute__((regparm(n)))指定最多可以使用n个寄存器(eax, edx, ecx)传递参数,n的范围是0~3,超过n时则将参数压入栈中(…...
⚡️如何在 React 和 Next.js 项目里优雅的使用 Zustand
前言 你是否曾感觉在 React 中管理状态简直是一场噩梦?如果你已经厌倦了不停地处理 props、context 和 hooks,那么现在是时候认识 Zustand 了。Zustand 是一个轻量级的状态管理库,它简化了你处理应用状态的方式。在这篇文章中,我…...
Pinpoint(APM)进阶--Pinot指标采集(System Metric/Inspector)
接上文 Pinpoint使用Pinot进行指标数据存储,Pinot流摄入需要Kafka 本文详解Kafka和Pinot的安装部署,以及Pinpoint的指标采集 Pinot 简介 Apache Pinot是一个实时分布式OLAP数据存储,专为低延迟、高吞吐量分析而构建,非常适合面…...
Mysql:使用binlog的一些常用技巧
1、如何查看binlog的存放路径 show variables like log% 执行结果: 2、如何清除binlog (1)按时间清除 purge binary logs before ‘2023-06-5 10:12:00’ (2)按文件文件名清除 purge binary logs to ‘mybinlog.0000…...
Electron 项目启动外部可执行文件的几种方式
Electron 项目启动外部可执行文件的几种方式 序言 在开发 Electron 应用程序时,有时需要启动外部的可执行文件(如 .exe 文件)。这可能是为了调用系统工具、运行第三方软件或者集成现有的应用程序。 Electron 提供了多种方式来启动外部可执行…...
前端开发中常用的包管理器(npm、yarn、pnpm、bower、parcel)
文章目录 1. npm (Node Package Manager)2. Yarn (Yarn Package Manager)3. pnpm4. Bower5. Parcel总结 前端开发中常用的包管理器主要有以下几个: 1. npm (Node Package Manager) 简介: npm 是 Node.js 的默认包管理器,也是最广泛使用的包…...
Linux入门:环境变量与进程地址空间
一. 环境变量 1. 概念 1️⃣基本概念: 环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数 如:我们在编写C/C代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪里&#x…...
【Jenkins实战】Windows安装服务启动失败
写此篇短文,望告诫后人。 如果你之前装过Jenkins,出于换域账号/本地帐号的原因想重新安装,你大概率会遇上一次Jenkins服务启动失败提示: Jenkins failed to start - Verify that you have sufficient privileges to start system…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...
基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
使用分级同态加密防御梯度泄漏
抽象 联邦学习 (FL) 支持跨分布式客户端进行协作模型训练,而无需共享原始数据,这使其成为在互联和自动驾驶汽车 (CAV) 等领域保护隐私的机器学习的一种很有前途的方法。然而,最近的研究表明&…...
MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...
【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具
第2章 虚拟机性能监控,故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令:jps [options] [hostid] 功能:本地虚拟机进程显示进程ID(与ps相同),可同时显示主类&#x…...
论文笔记——相干体技术在裂缝预测中的应用研究
目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术:基于互相关的相干体技术(Correlation)第二代相干体技术:基于相似的相干体技术(Semblance)基于多道相似的相干体…...
嵌入式学习笔记DAY33(网络编程——TCP)
一、网络架构 C/S (client/server 客户端/服务器):由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序,负责提供用户界面和交互逻辑 ,接收用户输入,向服务器发送请求,并展示服务…...
STM32HAL库USART源代码解析及应用
STM32HAL库USART源代码解析 前言STM32CubeIDE配置串口USART和UART的选择使用模式参数设置GPIO配置DMA配置中断配置硬件流控制使能生成代码解析和使用方法串口初始化__UART_HandleTypeDef结构体浅析HAL库代码实际使用方法使用轮询方式发送使用轮询方式接收使用中断方式发送使用中…...
手机平板能效生态设计指令EU 2023/1670标准解读
手机平板能效生态设计指令EU 2023/1670标准解读 以下是针对欧盟《手机和平板电脑生态设计法规》(EU) 2023/1670 的核心解读,综合法规核心要求、最新修正及企业合规要点: 一、法规背景与目标 生效与强制时间 发布于2023年8月31日(OJ公报&…...
