当前位置: 首页 > news >正文

MySQL中的幻读问题

1. 什么是幻读?

        幻读是一种数据库事务中可能出现的并发问题,具体表现为:在同一个事务中,前后两次查询的结果集不同,仿佛“幻影”一般,出现了原本不存在的数据

1.1 具体表现:

  1. 现象描述
    事务 A 在某个范围内查询了多行记录,并对这些记录进行了修改。
    随后,事务 B 在这个范围内插入了一条新记录。
    当事务 A 再次查询时,会发现多了一行之前不存在的记录。

  2. 幻读 VS 脏读/不可重复读

    • 脏读:事务读取到了另一个未提交事务的修改内容。
    • 不可重复读:事务前后读取同一行数据时,内容发生了变化。
    • 幻读:事务范围查询时,前后读取的结果集中多了新的“行”。

1.2 案例示例:

假设有一个 students 表,存储学生成绩记录:

idnamescore
1Alice85
2Bob90
  • 事务 A:启动事务并执行以下查询:

    SELECT * FROM students WHERE score > 80;

    返回结果为:

    idnamescore
    1Alice85
    2Bob90
  • 事务 B:插入一条新记录:

    INSERT INTO students (id, name, score) VALUES (3, 'Charlie', 88);
  • 事务 A:再次执行相同查询:

    SELECT * FROM students WHERE score > 80;

    返回结果为:

    idnamescore
    1Alice85
    2Bob90
    3Charlie88

这时,事务 A 发现了一条“幻影”数据(Charlie),这就是幻读

2. 快照读如何避免幻读?

2.1 什么是快照读?

        快照读指的是通过 MVCC(多版本并发控制)机制读取数据的一种方式,它读取的是某个时刻的“历史快照”,而不是当前最新的实际数据

  • 快照读依赖于 InnoDB 引擎实现的 MVCC。
  • 使用快照读时,即使其他事务对数据进行了插入或修改,当前事务依然能看到事务开始时的数据状态。

2.2 快照读的特点

  1. 读取旧版本数据
    每次查询的结果是事务开始时生成的快照,不受其他事务影响。

  2. 无需加锁
    快照读不需要像“当前读”那样使用锁机制,效率更高。

  3. 典型语句
    对于常见的 SELECT 语句,如果未显式使用锁机制,则是快照读。例如:

    SELECT * FROM students WHERE score > 80;

2.3 快照读如何避免幻读?

        在 可重复读(REPEATABLE READ)隔离级别下,MySQL 使用 MVCC 为事务生成一致性视图。

  • 每个事务在开始时,会记录当前的数据版本快照。
  • 对于后续的 SELECT 操作,返回的结果总是基于这一快照,即使其他事务对数据进行了增删改,也不会影响快照读的结果

举例说明:

假设在事务 A 开始时,students 表的状态如下:

idnamescore
1Alice85
2Bob90
  • 事务 A

    START TRANSACTION; SELECT * FROM students WHERE score > 80;

    返回结果为:

    idnamescore
    1Alice85
    2Bob90
  • 事务 B:插入一条新数据:

    INSERT INTO students (id, name, score) VALUES (3, 'Charlie', 88);
  • 事务 A:再次查询时:

    SELECT * FROM students WHERE score > 80;   

    返回结果依然是事务开始时的快照:

    idnamescore
    1Alice85
    2Bob90

这样,事务 A 避免了幻读,因为它读取的快照与事务开始时一致。

2.4 快照读的局限性

        快照读仅适用于普通查询操作。如果事务需要对数据进行修改(如 UPDATEDELETEINSERT),必须使用“当前读”,而当前读中可能会涉及幻读。

3. 当前读如何避免幻读?

3.1 什么是当前读?

        当前读指的是读取数据库中最新的、实时的数据版本,通常发生在需要对数据进行修改的场景中。例如,UPDATEDELETEINSERT 等操作就会触发当前读。

  • 当前读会通过加锁机制,确保数据的一致性。

典型语句:

SELECT * FROM students WHERE score > 80 FOR UPDATE;

3.2 当前读如何避免幻读?

        在 可重复读(REPEATABLE READ)隔离级别下,MySQL 的当前读会引入锁机制(尤其是间隙锁),避免其他事务在同一范围内插入新数据,从而解决幻读问题。

3.3 间隙锁(Gap Lock)的作用

  1. 定义
    间隙锁是 InnoDB 在事务中为避免幻读问题而引入的一种锁,它锁定的是一个范围(gap),而不仅仅是具体的行。

  2. 作用

    • 防止其他事务在锁定范围内插入新记录。
    • 确保同一事务中的前后查询结果集一致。
  3. 锁定范围示例
    假设查询条件为 WHERE score > 80,当前数据表如下:

    idnamescore
    1Alice85
    2Bob90

    当事务 A 使用当前读时:

    SELECT * FROM students WHERE score > 80 FOR UPDATE;

    InnoDB 会锁定范围 (80, ∞),此时:

    • 其他事务不能在这个范围内插入新记录,例如 INSERT INTO students (id, name, score) VALUES (3, 'Charlie', 88) 会被阻塞。
    • 保证当前事务不会读取到“幻影”数据。

案例说明:

假设有如下数据:

idnamescore
1Alice85
2Bob90
  • 事务 A:开启事务,并进行当前读:

    START TRANSACTION; SELECT * FROM students WHERE score > 80 FOR UPDATE;

    返回结果:

    idnamescore
    1Alice85
    2Bob90
  • 事务 B:试图插入一条数据:

    INSERT INTO students (id, name, score) VALUES (3, 'Charlie', 88);

    结果:事务 B 被阻塞,直到事务 A 提交或回滚。

  • 事务 A:再次查询时:

    SELECT * FROM students WHERE score > 80 FOR UPDATE;

    返回的结果仍然是:

    idnamescore
    1Alice85
    2Bob90

间隙锁保证了事务 A 的一致性,避免了幻读问题。

间隙锁的局限性:

  1. 可能引发性能问题

    • 间隙锁会阻止范围内的插入操作,如果锁定范围较大,可能造成事务等待时间过长。
  2. 事务隔离级别
    间隙锁只在 可重复读 隔离级别下有效,在更低的隔离级别(如读已提交)中不适用。

4. 幻读是否被完全解决?

        尽管 可重复读(REPEATABLE READ)隔离级别通过快照读和当前读(间隙锁)在很大程度上避免了幻读,但并不能完全解决所有场景下的幻读问题。以下是详细的分析和示例。

4.1 幻读可能依然存在的场景

在一些复杂事务操作中,即使使用间隙锁,幻读问题依然可能发生。例如:

  • 并发事务修改数据的交叉操作:涉及范围查询时,锁范围与实际查询范围不完全匹配。
  • 间隙锁未覆盖所有查询条件:事务锁定的范围不能完全避免其他事务插入新的数据。

案例分析:复杂事务下的幻读

假设 students 表当前的状态为:

idnamescore
1Alice85
2Bob90

场景描述

  • 事务 A:希望对分数大于 80 的学生记录进行处理。
  • 事务 B:在事务 A 操作期间插入了一条符合条件的记录。

具体操作:

  1. 事务 A

    START TRANSACTION; SELECT * FROM students WHERE score > 80 FOR UPDATE;

    返回结果:

    idnamescore
    1Alice85
    2Bob90

    此时,间隙锁锁定了 (80, ∞) 范围。

  2. 事务 B
    在锁定范围之外插入了一条分数为 80 的记录:

    INSERT INTO students (id, name, score) VALUES (3, 'Charlie', 80);

    结果:事务 B 的插入成功,因为分数为 80 不在事务 A 锁定的范围 (80, ∞) 内。

  3. 事务 A
    对分数大于等于 80 的记录进行处理:

    UPDATE students SET score = score + 5 WHERE score >= 80;

    结果:事务 A 的操作覆盖了事务 B 插入的记录(Charlie),导致了一种幻读现象。

4.2 为什么幻读未被完全解决?

  1. 锁范围与查询条件不完全匹配
    间隙锁锁定的范围是基于查询条件计算的,但并不总是能够覆盖所有潜在的记录。

  2. 复杂的业务场景
    当查询涉及范围变化(如 >=BETWEEN),锁的逻辑变得复杂且难以避免边界情况。

4.3 如何完全解决幻读?

在某些关键场景下,为了避免幻读问题,可以使用更高的隔离级别或额外的手段:

  1. 可串行化隔离级别(SERIALIZABLE)

    • 串行化隔离级别会对所有的读操作加锁,避免了所有类型的幻读问题。
    • 代价:显著的性能开销,因为所有事务会变成串行执行。
  2. 手动加锁
    在事务操作中显式加表锁或行锁,确保范围锁定完全覆盖。

  3. 业务逻辑约束
    优化业务设计,避免范围查询可能带来的并发插入问题。

具体示例:可串行化避免幻读

事务 A

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; 
START TRANSACTION; SELECT * FROM students WHERE score >= 80 FOR UPDATE;

事务 B
试图插入符合条件的记录:

INSERT INTO students (id, name, score) VALUES (3, 'Charlie', 88);

结果:事务 B 被阻塞,直到事务 A 提交或回滚,避免了幻读现象。

5. 总结

5.1 幻读问题的核心

        幻读是事务中一种特殊的数据不一致现象,通常出现在并发环境下。当事务在范围查询中遇到其他事务插入或修改的符合条件的新数据时,可能会导致前后查询结果不一致,这种现象称为幻读。

5.2 如何避免幻读?

  1. 快照读

    • 使用多版本并发控制(MVCC),通过历史版本实现一致性读,从根本上避免了幻读。
    • 快照读适用于查询操作,如 SELECT
  2. 当前读

    • 通过加锁(如间隙锁)避免并发插入或修改操作导致幻读问题。
    • 当前读适用于更新操作,如 UPDATEDELETE 和带 FOR UPDATESELECT
  3. 间隙锁(Gap Lock)

    • 锁定范围而非具体记录,阻止其他事务在锁定范围内插入新数据,从而在可重复读(REPEATABLE READ)隔离级别下避免幻读。
  4. 可串行化隔离级别(SERIALIZABLE)

    • 通过更严格的锁机制,彻底避免了所有类型的幻读问题,但以性能开销为代价。

5.3 幻读是否被完全解决?

  1. 在可重复读隔离级别下,虽然快照读和间隙锁大大减少了幻读的发生,但仍然存在无法完全覆盖的场景,尤其是复杂的范围查询。
  2. 要彻底解决幻读问题,需要更高的隔离级别(如可串行化)或优化业务逻辑。

具体实例说明

  1. 在可重复读隔离级别下:

    • 快照读避免了幻读,但不能适用于需要修改数据的场景。
    • 当前读通过间隙锁避免了大多数幻读,但可能因锁范围不匹配而产生边界问题。
  2. 在可串行化隔离级别下:

    • 完全阻止了幻读,但事务效率可能显著降低。

5.4 应对幻读的策略

  1. 选择合适的隔离级别

    • 大多数情况下,可重复读隔离级别(REPEATABLE READ)已经能够满足需求。
    • 在需要强一致性时,考虑使用可串行化隔离级别。
  2. 优化锁机制

    • 根据具体业务需求,合理选择加锁范围,避免过大或过小。
  3. 业务逻辑调整

    • 通过限制并发操作或使用显式锁,降低幻读发生的概率。

相关文章:

MySQL中的幻读问题

1. 什么是幻读? 幻读是一种数据库事务中可能出现的并发问题,具体表现为:在同一个事务中,前后两次查询的结果集不同,仿佛“幻影”一般,出现了原本不存在的数据。 1.1 具体表现: 现象描述 事务 A…...

AI后端工程师面试题的内容

AI后端工程师面试题主要包括以下几个方面的内容‌: ‌一、技术基础和项目经验‌: ‌1. 微服务架构的理解和应用‌:请描述你对微服务架构的理解,并举例说明一个你参与过的微服务项目,阐述你在该项目中扮演的角色和所承…...

MFC工控项目实例三十五读取数据库数据

点击按钮打开文件夹中的数据文件生成曲线 相关代码 void CSEAL_PRESSUREDlg::OnTesReport() {CFileDialog dlgOpen(TRUE/*TRUE打开,FALSE保存*/,0,0,OFN_NOCHANGEDIR|OFN_FILEMUSTEXIST,"All Files(mdb.*)|*.*||",//文件过滤器NULL);CString mdb_1, m…...

OpenWrt -制作ubifs文件系统的固件

目的 创建一个ubifs为文件系统的镜像 将backup目录中的内容打包成ubifs文件系统。 ubifs的分区定义 ubi-backup.cfg 文件内容如下, [backup] modeubi imagenand-ipq6018-single.img vol_id0 vol_typedynamic vol_namebackup [bkver] modeubi imagebackup.ubifs v…...

C++ - 继承

继承的基本概念 继承就是一种代码的复用. 子类通过继承父类, 就能使用父类的变量, 方法. 学生和老师这两种身份, 他们都有共同的属性: 他们都有名称, 年龄, 性别 .... 当然他们也有各种独有的属性, 学生有学号, 老师有工号 .... 对于这些共有的属性, 我们可以将它们提取出来: …...

华为服务器使用U盘重装系统

一、准备工作 下载官方系统(注意服务器CPU的架构是x86-64还是aarch64,不然可能报意想不到的错)制作启动U盘(下载rufus制作工具,注意文件系统选FAT32还是NTFS) 二、安装步骤 将U盘插入USB接口重启服务器…...

网络分层模型( OSI、TCP/IP、五层协议)

1、网络分层模型 计算机网络是一个极其复杂的系统。想象一下最简单的情况:两台连接在网络上的计算机需要相互传输文件。不仅需要确保存在一条传输数据的通路,还需要完成以下几项工作: 发起通信的计算机必须激活数据通路,这包括发…...

前端开发 之 15个页面加载特效上【附完整源码】

文章目录 一:彩球环绕加载特效1.效果展示2.HTML完整代码 二:跷跷板加载特效1.效果展示2.HTML完整代码 三:两个圆形加载特效1.效果展示2.HTML完整代码 四:半环加载特效1.效果展示2.HTML完整代码 五:音乐波动加载特效1.效…...

Spring Boot使用JDK 21虚拟线程

JDK 21引入的虚拟线程(Virtual Threads)是 Project Loom 的一部分,旨在显著简化并发编程并提高 Java 应用的可扩展性。以下是虚拟线程的主要特点: 1. 概念 虚拟线程是轻量级线程,与传统的操作系统线程不同&#xff0…...

《从0到1常用Map集合核心摘要 + 不深不浅底层核心》

《从0到1常用Map集合核心摘要不深不浅底层核心》 前置知识 什么是键值对 ​ 键值对是一种数据结构,键是唯一标识,值是对应数据,用来快速查找信息。例: {"name": "Alice"},键是name,…...

12 设计模式之工厂方法模式

一、什么是工厂方法模式? 1.定义 在软件开发中,设计模式 是解决常见软件设计问题的最佳实践。而 工厂方法模式(Factory Method Pattern) 作为创建型设计模式之一,常常被用来解决对象创建问题。它通过将对象的创建交给…...

spaCy 入门与实战:强大的自然语言处理库

spaCy 入门与实战:强大的自然语言处理库 spaCy 是一个现代化、工业级的自然语言处理(NLP)库,以高效、易用和功能丰富著称。它被广泛应用于文本处理、信息提取和机器学习任务中。本文将介绍 spaCy 的核心功能,并通过一…...

python包的管理和安装——笔记

1.列出包 pip list pip freeze 用这2个可以查看当前python 下所有的包和版本,还有下载地址 如果只是想导出当前的环境 可以用 2.安装pipreqs pip install pipreqs,pipreqs ./可以导出当前项目的包这个包 遇到编码报错 pipreqs ./ --encodingutf8 p…...

Vue前端页面内嵌套本项目iframe窗口的通信传输方式

一、目的 想要在iframe中使用本项目页面、并能够与其父页面组件实现实时通信。Vue前端页面内嵌套本项目iframe窗口的通信传输方式-星林社区 https://www.jl1mall.com/forum/PostDetail?postId20241202172800023969 二、iframe通信方式 1.接收消息 页面需要监听 message 事件…...

【WEB开发.js】addEventListener事件监听器的绑定和执行次数的问题(小心踩坑)

假设我们有一个按钮,用户点击该按钮后,会选择一个文件,且我们希望每次点击按钮时只触发一次文件处理。下面我会给你一个简单的例子,展示放在函数内部和放在函数外部的区别。 1. 将事件监听器放在函数内部(问题的根源&…...

用于LiDAR测量的1.58um单芯片MOPA(一)

--翻译自M. Faugeron、M. Krakowski1等人2014年的文章 1.简介 如今,人们对高功率半导体器件的兴趣日益浓厚,这些器件主要用于遥测、激光雷达系统或自由空间通信等应用。与固态激光器相比,半导体器件更紧凑且功耗更低,这在低功率供…...

【GPT】代谢概念解读

以下是对代谢中分解代谢和合成代谢两个概念的深入解读,用简单易懂的方式展开说明: 1. 分解代谢(Catabolism) 什么是分解代谢? 分解代谢是身体把大分子“拆开”的过程。就像把一个三明治分解成面包片、肉片和菜叶&#…...

Devops-git篇-01-git环境配置

环境配置 设置用户签名 配置用户名: git config --global user.name 你的用户名 配置邮箱: git config --global user.email 注册的邮箱 配置好之后,可以用git config --global --list命令查看配置是否OK $ git config --global --list u…...

STM32 HAL库开发学习1.STM32CubeMX 新建工程

STM32 HAL库开发学习1.STM32CubeMX 新建工程 一、 STM32 CubeMX 下载二、CubeMX 功能介绍1. 固件包路径设置2. 新建工程 三、创建项目实例1. 新建项目2. GPIO 管脚设置3. GPIO 窗口配置4. 调试设置5. 时钟配置6. 项目管理(1)项目信息(2&#…...

JS学习(2)(浏览器执行JS过程、JS的ECMAScript、DOM、BOM)

目录 一、浏览器如何执行JS? (1)浏览器主要的组成部分。 1、渲染引擎。 2、JS引擎。 (2)演示。 二、JS的组成。 (1)JS主要由三部分组成。 1、JS基础。 2、JS-API。 (2)EC…...

java 实现excel文件转pdf | 无水印 | 无限制

文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析

这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...

服务器硬防的应用场景都有哪些?

服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式,避免服务器受到各种恶意攻击和网络威胁,那么,服务器硬防通常都会应用在哪些场景当中呢? 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...

【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】

1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件(System Property Definition File),用于声明和管理 Bluetooth 模块相…...

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...

全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比

目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...

MySQL用户和授权

开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务: test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...

从 GreenPlum 到镜舟数据库:杭银消费金融湖仓一体转型实践

作者:吴岐诗,杭银消费金融大数据应用开发工程师 本文整理自杭银消费金融大数据应用开发工程师在StarRocks Summit Asia 2024的分享 引言:融合数据湖与数仓的创新之路 在数字金融时代,数据已成为金融机构的核心竞争力。杭银消费金…...

MyBatis中关于缓存的理解

MyBatis缓存 MyBatis系统当中默认定义两级缓存:一级缓存、二级缓存 默认情况下,只有一级缓存开启(sqlSession级别的缓存)二级缓存需要手动开启配置,需要局域namespace级别的缓存 一级缓存(本地缓存&#…...

Python竞赛环境搭建全攻略

Python环境搭建竞赛技术文章大纲 竞赛背景与意义 竞赛的目的与价值Python在竞赛中的应用场景环境搭建对竞赛效率的影响 竞赛环境需求分析 常见竞赛类型(算法、数据分析、机器学习等)不同竞赛对Python版本及库的要求硬件与操作系统的兼容性问题 Pyth…...