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 = 0id = 5这两行,因为事务 B 的修改已经提交,Q2 需要读到最新值。
  • 接着,在 T4 时刻,事务 C 插入了一行(1,1,5)。当事务 A 执行 Q3 查询时,就会看到id = 0id = 1id = 5这三行。

这里,Q3 读到id = 1这一行的现象就是幻读。需要注意的是,在可重复读隔离级别下,普通查询是快照读,不会看到其他事务插入的数据,幻读仅在当前读下才会出现。而且,幻读特指新插入的行,像事务 B 的修改结果被事务 A 之后的查询用当前读看到,并不属于幻读范畴。


二、幻读带来的问题

(一)语义问题

从语义角度来看,幻读会导致事务的加锁声明失去意义。

就像事务 A 在 T1 时刻声明要锁住所有d = 5的行,禁止其他事务进行读写操作。然而,由于幻读的存在,事务 B 可以修改id = 0这一行的d值为 5,事务 C 还能插入新的(1,1,5)行,这显然破坏了事务 A 的加锁语义。

我们通过一个详细的场景来进一步说明。假设事务 B 和事务 C 在执行修改和插入操作时,还分别执行了其他相关操作:

session Asession Bsession C
T1begin; select * from t where d = 5 for update; / * Q1 * /
T2update t set d = 5 where id = 0; update t set c = 5 where id = 0;

T3select * from t where d = 5 for update; / * Q2 * /
T4insert into t values(1,1,5); update t set c = 5 where id = 1;
T5select * from t where d = 5 for update; / * Q3 * /
T6commit;

事务 B 的第二条语句update t set c = 5 where id = 0,语义是修改id = 0d = 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 = 0id = 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 的解决方案。

  • 在实际应用中,我们需要深入理解这些概念,根据业务需求合理选择事务隔离级别和配置。如果对间隙锁等机制理解不足,可能会导致生产库上出现死锁等问题,影响系统的性能和稳定性。希望本文能够帮助读者更好地掌握幻读相关知识,在数据库设计和开发中做出明智的决策,构建出更加可靠、高效的数据库应用系统。

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

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

相关文章

【再谈设计模式】建造者模式~对象构建的指挥家

一、引言 在软件开发的世界里,创建对象是一项基本且频繁的操作。然而,当对象的构造变得复杂,涉及众多属性和初始化步骤时,传统的构造函数方式往往会让代码陷入混乱的泥沼。就如同搭建一座复杂的建筑,若没有合理的规划和…

三、模板与配置(下)

三、模板与配置 8、WXSS模板样式-全局样式和局部样式 类型说明适用情景注意点全局样式定义在 app.wxss 中的样式,作用于每一个页面。当有一些通用的样式规则需要应用于整个小程序时,比如全局的字体大小、颜色、布局等。全局样式可能会被局部样式覆盖&a…

SQL面试题——抖音SQL面试题 主播播出时长

主播播出时长 现有如下数据,主播id、房间号、播出的批次号,每个批次号进出房间的时间戳、分区时间: 每一次直播都有一个上播和下播,每个房间里,同一个批次号会有两条数据,分别记录了上播和下播时间,求每个主播的播出时长? 通过上面的数据,可以清晰的看出,同一个批次…

大语言模型LLM综述

一、LM主要发展阶段 1.1、统计语言模型SLM 基于统计学习方法,基本思想是基于马尔可夫假设HMM建立词概率预测模型。如n-gram语言模型 1.2、神经语言模型NLM 基于神经网络来做词的分布式表示。如word2vec模型 1.3、 预训练语言模型PLM 预训练一个网络模型来做词表…

用 Python 从零开始创建神经网络(七):梯度下降(Gradient Descent)/导数(Derivatives)

梯度下降(Gradient Descent)/导数(Derivatives) 引言1. 参数对输出的影响2. 斜率(The Slope)3. 数值导数(The Numerical Derivative)4. 解析导数(The Analytical Derivat…

防爆手机市场“百花齐放”,该怎么选?

在危险作业场景,如石化煤矿,通讯设备采购关系到提高生产效率‌,保障安全生产‌,‌符合法规要求‌。在应急救援通信场景,通讯设备采购,与保障救援行动的效率和准确性息息相关。而通信设备的性能参数是评估其…

似然函数解析

从贝叶斯定理引出似然函数 最大似然估计用来估计均值方差的 文心对似然函数的解释,注意 抛出正反的概率,就是固定均值方差的条件概率密度了

记录配置ubuntu18.04下运行ORBSLAM3的ros接口的过程及执行单目imu模式遇到的问题(详细说明防止忘记)

今天的工作需要自己录制的数据集来验证昨天的标定结果 用ORBSLAM3单目imu模式运行,mentor给的是一个rosbag格式的数据包,配置过程出了几个问题记录一下,沿配置流程写。 一.orbslam3编译安装 1.首先是安装各种依赖 这里不再赘述&#xff0…

实验5:网络设备发现、管理和维护

实验5:网络设备发现、管理和维护 实验目的及要求: 通过实验,掌握Cisco 路由器和交换机的IOS配置管理。自动从NTP服务器获取时间信息。能够利用TFTP服务器实现路由器和交换机配置文件的备份和恢复。同时验证CDP协议和LLDP协议的网络参数。完…

前端(2)——快速入门CSS

参考: 罗大富 CSS 参考手册 | 菜鸟教程 CSS 参考手册 1. CSS CSS全名是层叠样式表,中文名层叠样式表。用于定义网页样式和布局的样式表语言。 通过 CSS,你可以指定页面中各个元素的颜色、字体、大小、间距、边框、背景等样式,…

yolo标签自动标注(使用python和yolo方法)

yolo代码自动标注 1.引言1.初阶“自动标注”,给每个图像都生成一个固定的标注文件,进而在labglimg中对矩形框进行微调,减少标注的工作量2.高阶自动标注,利用我们训练好的(但是没有特别精准的)yolo文件先对每…

jmeter常用配置元件介绍总结之线程组

系列文章目录 安装jmeter jmeter常用配置元件介绍总结之线程组 1.线程组(用户)1.1线程组1.1.setUp线程组和tearDown线程组1.2.Open Model Thread Group(开放模型线程组)1.3.bzm - Arrivals Thread Group(到达线程组)1.4.jpgc - Ultimate Thread Group(终极线程组)1.5.jpgc - St…

spring gateway 动态路由

##yml配置 spring:application:name: public-gateway # cloud: # gateway: # routes: # - id: mybatis-plus-test # 路由的唯一标识 # uri: http://192.168.3.188:9898 # 目标服务的地址 # predicates: # - Path/test/** # 匹配…

NVT新能德科技入职测评SHL题库更新:数字推理+演绎推理高分答案、真题解析

新能德的入职Verify测评主要考察应聘者的逻辑推理能力、数学能力、数据分析能力以及处理信息的能力。根据搜索结果,测评通常包含以下几个部分: 1. **语言理解**:这部分包括阅读理解、逻辑填空和语句排序。要求应聘者在17分钟内完成30题&#…

学法减分交管12123模拟练习小程序源码前端和后端和搭建教程

交管推出个学法减分,每个驾驶员可以把被扣的6分,以看视频答题的形式学习回来,然后答题这个一共二十道题每道题60秒,有好多人不会,用咱们的小程序就可以模拟练习强化练习,还有拍照识别题目找到正确答案&…

element-plus <el-date-picker>日期选择器踩坑!!!!

我怎么一上午踩两个坑&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff08;大声bb&#xff09; 原来的vue2老项目是这样写的 <el-form-item label"时间" prop"time"><el-date-pickerv-model"addForm.time"typ…

JS的学习与使用

JS的学习与使用 一 什么是Javascript&#xff1f; Javascript是一门跨平台&#xff0c;面向对象的脚本语言&#xff0c;是用来控制网页行为的&#xff0c;它能使网页可以交互 java与Javascript是完全不同的语言&#xff0c;不论是概念还是设计&#xff0c;但是基础语法类似 E…

ubuntu20.04 解决Pytorch默认安装CPU版本的问题

ubuntu20.04 解决Pytorch默认安装CPU版本的问题 在使用Anaconda安装支持CUDA的PyTorch版本时&#xff0c;遇到只能安装CPU版本的PyTorch是一个常见问题。这通常由于Anaconda环境配置、镜像源设置不当或版本匹配问题导致。以下是详尽的解决方案和步骤&#xff0c;以确保能够正确…

【操作系统】守护进程

一、守护进程的概念 守护进程是一个在后台运行并且不受任何终端控制的进程 二、自己实现守护进程 1.预备知识 &#xff08;1&#xff09;/dev/null /dev/null是一个特殊的设备文件&#xff0c;往这个文件里写不进去任何数据&#xff0c;也读不出来任何数据 因此&#xff0…

【数据结构与算法】第12课—数据结构之归并排序

文章目录 1. 归并排序2. 计数排序3. 排序算法复杂度及稳定性分析在这里插入图片描述 1. 归并排序 分治法&#xff08;Divide and Conquer&#xff09;是一种重要的算法设计策略&#xff0c;其核心思想是将一个复杂的大问题分解为若干个小规模的子问题&#xff0c;递归地解决这些…