MySQL 记录锁+间隙锁可以防止删除操作而导致的幻读吗?
文章目录
- 什么是幻读?
- 实验验证
- 加锁分析
- 总结
什么是幻读?
首先来看看 MySQL 文档是怎么定义幻读(Phantom Read)的:
The so-called phantom problem occurs within a transaction when the same query produces different sets of rows at different times.
For example, if a SELECT is executed twice, but returns a row the second time that was not returned the first time, the row is a “phantom” row.
翻译:当同一个查询在不同的时间产生不同的结果集时,事务中就会出现所谓的幻象问题。例如,如果 SELECT 执行了两次,但第二次返回了第一次没有返回的行,则该行是“幻像”行。
举个例子,假设一个事务在 T1 时刻和 T2 时刻分别执行了下面查询语句,途中没有执行其他任何语句:
SELECT * FROM t_test WHERE id > 100;
只要 T1 和 T2 时刻执行产生的结果集是不相同的,那就发生了幻读的问题,比如:
T1 时间执行的结果是有 5 条行记录,而 T2 时间执行的结果是有 6 条行记录,那就发生了幻读的问题。
T1 时间执行的结果是有 5 条行记录,而 T2 时间执行的结果是有 4 条行记录,也是发生了幻读的问题。
MySQL 是怎么解决幻读的?
MySQL InnoDB 引擎的默认隔离级别虽然是「可重复读」,但是它很大程度上避免幻读现象(并不是完全解决了,详见这篇文章 (opens new window)),解决的方案有两种:
- 针对快照读(普通 select 语句),是通过 MVCC 方式解决了幻读,因为可重复读隔离级别下,事务执行过程中看到的数据,一直跟这个事务启动时看到的数据是一致的,即使中途有其他事务插入了一条数据,是查询不出来这条数据的,所以就很好了避免幻读问题。
- 针对当前读(select … for update 等语句),是通过 next-key lock(记录锁+间隙锁)方式解决了幻读,因为当执行 select … for update 语句的时候,会加上 next-key lock,如果有其他事务在 next-key lock 锁范围内插入了一条记录,那么这个插入语句就会被阻塞,无法成功插入,所以就很好了避免幻读问题。
实验验证
接下来,来验证「 MySQL 记录锁+间隙锁可以防止删除操作而导致的幻读问题」的结论。
实验环境:MySQL 8.0 版本,可重复读隔离级。
现在有一张用户表(t_user),表里只有一个主键索引,表里有以下行数据:
现在有一个 A 事务执行了一条查询语句,查询到年龄大于 20 岁的用户共有 6 条行记录。
此时,B 事务的删除语句就陷入了等待状态,说明是无法进行删除的。
因此,MySQL 记录锁+间隙锁可以防止删除操作而导致的幻读问题。
加锁分析
问题来了,A 事务在执行 select … for update 语句时,具体加了什么锁呢?
我们可以通过 select * from performance_schema.data_locks\G;
这条语句,查看事务执行 SQL 过程中加了什么锁。
输出的内容很多,共有 11 行信息,我删减了一些不重要的信息:
从上面输出的信息可以看到,共加了两种不同粒度的锁,分别是:
- 表锁(LOCK_TYPE: TABLE):X 类型的意向锁;
- 行锁(LOCK_TYPE: RECORD):X 类型的 next-key 锁;
这里我们重点关注「行锁」,图中 LOCK_TYPE 中的 RECORD 表示行级锁,而不是记录锁的意思:
- 如果 LOCK_MODE 为 X,说明是 next-key 锁;
- 如果 LOCK_MODE 为 X, REC_NOT_GAP,说明是记录锁;
- 如果 LOCK_MODE 为 X, GAP,说明是间隙锁;
然后通过 LOCK_DATA 信息,可以确认 next-key 锁的范围,具体怎么确定呢?
- 根据我的经验,如果 LOCK_MODE 是 next-key 锁或者间隙锁,那么 LOCK_DATA 就表示锁的范围最右值,而锁范围的最左值为 LOCK_DATA 的上一条记录的
因此,此时事务 A 在主键索引(INDEX_NAME : PRIMARY)上加了 10 个 next-key 锁,如下:
- X 型的 next-key 锁,范围:(-∞, 1]
- X 型的 next-key 锁,范围:(1, 2]
- X 型的 next-key 锁,范围:(2, 3]
- X 型的 next-key 锁,范围:(3, 4]
- X 型的 next-key 锁,范围:(4, 5]
- X 型的 next-key 锁,范围:(5, 6]
- X 型的 next-key 锁,范围:(6, 7]
- X 型的 next-key 锁,范围:(7, 8]
- X 型的 next-key 锁,范围:(8, 9]
- X 型的 next-key 锁,范围:(9, +∞]
这相当于把整个表给锁住了,其他事务在对该表进行增、删、改操作的时候都会被阻塞。
只有在事务 A 提交了事务,事务 A 执行过程中产生的锁才会被释放。
为什么只是查询年龄 20 岁以上行记录,而把整个表给锁住了呢?
这是因为事务 A 的这条查询语句是全表扫描,锁是在遍历索引的时候加上的,并不是针对输出的结果加锁。
因此,在线上在执行 update、delete、select … for update 等具有加锁性质的语句,一定要检查语句是否走了索引,如果是全表扫描的话,会对每一个索引加 next-key 锁,相当于把整个表锁住了,这是挺严重的问题。
如果对 age 建立索引,事务 A 这条查询会加什么锁呢?
接下来,我对 age 字段建立索引,然后再执行这条查询语句:
接下来,继续通过 select * from performance_schema.data_locks\G;
这条语句,查看事务执行 SQL 过程中加了什么锁。
具体的信息,我就不打印了,我直接说结论吧。
因为表中有两个索引,分别是主键索引和 age 索引,所以会分别对这两个索引加锁。二级索引树加next-key lock,主键索引树加record lock
主键索引会加如下的锁:
- X 型的记录锁,锁住 id = 2 的记录;
- X 型的记录锁,锁住 id = 3 的记录;
- X 型的记录锁,锁住 id = 5 的记录;
- X 型的记录锁,锁住 id = 6 的记录;
- X 型的记录锁,锁住 id = 7 的记录;
- X 型的记录锁,锁住 id = 8 的记录;
分析 age 索引加锁的范围时,要先对 age 字段进行排序。
age 索引加的锁:
- X 型的 next-key lock,锁住 age 范围 (19, 21] 的记录;
- X 型的 next-key lock,锁住 age 范围 (21, 21] 的记录;
- X 型的 next-key lock,锁住 age 范围 (21, 23] 的记录;
- X 型的 next-key lock,锁住 age 范围 (23, 23] 的记录;
- X 型的 next-key lock,锁住 age 范围 (23, 39] 的记录;
- X 型的 next-key lock,锁住 age 范围 (39, 43] 的记录;
- X 型的 next-key lock,锁住 age 范围 (43, +∞] 的记录;
化简一下,age 索引 next-key 锁的范围是 (19, +∞]
可以看到,对 age 字段建立了索引后,查询语句是索引查询,并不会全表扫描,因此不会把整张表给锁住
总结一下,在对 age 字段建立索引后,事务 A 在执行下面这条查询语句后,主键索引和 age 索引会加下图中的锁。
事务 A 加上锁后,事务 B、C、D、E 在执行以下语句都会被阻塞。
总结
在 MySQL 的可重复读隔离级别下,针对当前读的语句会对索引加记录锁+间隙锁,这样可以避免其他事务执行增、删、改时导致幻读的问题。
有一点要注意的是,在执行 update、delete、select … for update 等具有加锁性质的语句,一定要检查语句是否走了索引,如果是全表扫描的话,会对每一个索引加 next-key 锁,相当于把整个表锁住了,这是挺严重的问题。
相关文章:

MySQL 记录锁+间隙锁可以防止删除操作而导致的幻读吗?
文章目录什么是幻读?实验验证加锁分析总结什么是幻读? 首先来看看 MySQL 文档是怎么定义幻读(Phantom Read)的: The so-called phantom problem occurs within a transaction when the same query produces different sets of r…...

【分库分表】企业级分库分表实战方案与详解(MySQL专栏启动)
📫作者简介:小明java问道之路,2022年度博客之星全国TOP3,专注于后端、中间件、计算机底层、架构设计演进与稳定性建设优化,文章内容兼具广度、深度、大厂技术方案,对待技术喜欢推理加验证,就职于…...

(考研湖科大教书匠计算机网络)第五章传输层-第五节:TCP拥塞控制
获取pdf:密码7281专栏目录首页:【专栏必读】考研湖科大教书匠计算机网络笔记导航 文章目录一:拥塞控制概述二:拥塞控制四大算法(1)慢开始和拥塞避免A:慢启动(slow start)…...
13.使用自动创建线程池的风险,要自己创建为好
自动创建线程池就是直接调用 Executors去new默认的那几个线程池,但是会出现一定的风险,线程池里面会用到队列,也会跟线程池自身有关,所以要从队列和线程池两个方面去解析。 1.了解线程池的队列 线程池的内部结构主要由四部分组成…...

【项目设计】—— 负载均衡式在线OJ平台
目录 一、项目的相关背景 二、所用技术栈和开发环境 三、项目的宏观结构 四、compile_server模块设计 1. 编译服务(compiler模块) 2. 运行服务(runner模块) 3. 编译并运行服务(compile_run模块) 4…...

Docker学习笔记
1:docker安装步骤Linux 2:docker安装步骤Windows 3:docker官方文档 4:docker官方远程仓库 docker常用命令 1: docker images----查看docker中安装的镜像 2: docker pull nginx------在docker中安装Nginx镜…...

【爬虫理论实战】详解常见头部反爬技巧与验证方式 | 有 Python 代码实现
以下是常见头部反爬技巧与验证方式的大纲: User-Agent 字段的伪装方式,Referer 字段的伪装方式,Cookie 字段的伪装方式。 文章目录1. ⛳️ 头部反爬技巧1.1. User-Agent 字段&User-Agent 的作用1.2. 常见 User-Agent 的特征1.3. User-Age…...

基于SpringBoot+Vue的鲜花商场管理系统
【辰兮要努力】:hello你好我是辰兮,很高兴你能来阅读,昵称是希望自己能不断精进,向着优秀程序员前行! 博客来源于项目以及编程中遇到的问题总结,偶尔会有读书分享,我会陆续更新Java前端、后台、…...
华为OD机试 - 静态扫描最优成本(JS)
静态扫描最优成本 题目 静态扫描快速识别源代码的缺陷,静态扫描的结果以扫描报告作为输出: 文件扫描的成本和文件大小相关,如果文件大小为 N ,则扫描成本为 N 个金币扫描报告的缓存成本和文件大小无关,每缓存一个报告需要 M 个金币扫描报告缓存后,后继再碰到该文件则不…...

多层感知机
多层感知机理论部分 本文系统的讲解多层感知机的pytorch复现,以及详细的代码解释。 部分文字和代码来自《动手学深度学习》!! 目录多层感知机理论部分隐藏层多层感知机数学逻辑激活函数1. ReLU函数2. sigmoid函数3. tanh函数多层感知机的从零…...
python在windows调用svn-pysvn
作为EBS开发人员,开发工具用的多,部署代码类型多,管理程序麻烦,操作繁琐,一直是我最讨厌的事情。部署一次程序要使用好几个工具,改来改去,上传下载,实在难受。 扣了一下python&#…...

office365 word 另存为 pdf 的注意事项和典型设置
0. 操作环境介绍 Office 版本:Office 365 版本 不同版本的操作可能有所不同 1. 基本操作 – 另存为 pdf 【文件】 --> 【另存为】,选择适当的文件路径、文件名保存类型选择【PDF】点击【保存】 1. 导出的pdf包含目录标签 word中,可使用…...

Spring IoC容器之常见常用注解以及注解编程模型简介
一、全文概览 本篇文章主要学习记录Spring中的核心注解,罗列常见常用的注解以及Spring中的注解编程模型介绍 二、核心注解 1、Spring模式注解 常用注解场景描述Spring起始支持版本Component通用组件模式注解,是所有组件类型注解的元注解Spring 2.5Repo…...

超详细讲解文件函数
超详细讲解文件函数!!!!字符输入/输出函数fgetcfputc文本行输入/输出函数fgetsfputs格式化输入/输出函数fscanffprintf二进制输入/输出函数freadfwrite打开/关闭文件函数fopenfclose字符输入/输出函数 fgetc fgetc函数可以从指定…...

【挣值分析】
名称解释 拼写解释PV计划费用,预估预算EV挣值,实际预估预算AC实际费用,实际花费CV成本偏差 (EV - AC)SV进度偏差(EV - PV)CPI成本绩效指数 (EV / AC)SPI进度绩效指数 &a…...
Python3-基础语法
Python3 基础语法 编码 默认情况下,Python 3 源码文件以 UTF-8 编码,所有字符串都是 unicode 字符串。 当然你也可以为源码文件指定不同的编码: # -*- coding: cp-1252 -*-上述定义允许在源文件中使用 Windows-1252 字符集中的字符编码&…...

【计算机网络】数据链路层(下)
文章目录媒体接入控制媒体接入控制-静态划分信道随机接入 CSMACD协议随机接入 CSMACA协议MAC地址MAC地址作用MAC地址格式MAC地址种类MAC地址的发送顺序单播MAC地址广播MAC地址多播MAC地址随机MAC地址IP地址区分网络编号IP地址与MAC地址的封装位置转发过程中IP地址与MAC地址的变…...
系统分析师考试大纲
系统分析师考试大纲 1.考试目标 通过本考试的合格人员应熟悉应用领域的业务,能分析用户的需求和约束条件,写出信息系统需求规格说明书,制订项目开发计划,协调信息系统开发与运行所涉及的各类人员;能指导制…...
2023上半年软考报名时间已定,你准备好了吗?
港城软考公众号于2023年2月17日发布了2023年度计算机软考工作计划,从该计划内容得知,2023年计算机软考上半年报名3月13日开始,请相关报考人员提前做好报名准备工作。 其他各省市还暂未公布2023上半年软考报名时间,每年都有很多…...

DPDK — Userspace PMD 源码分析
目录 文章目录目录PMD driver 通过 IGB_UIO 与 UIO 进行交互注册一个 UIO 设备PMD 的应用层实现PMD 同样支持中断处理方式PMD driver 通过 IGB_UIO 与 UIO 进行交互 IGB_UIO 内核模块的另一个主要功能就是让用于态的 PMD 网卡驱动程序得以与 UIO 进行交互。对于 PMD 的实现来说…...

Linux--命令行参数和环境变量
1.命令行参数 Linux 命令行参数基础 1.1参数格式 位置参数:无符号,按顺序传递(如 ls /home/user 中 /home/user 是位置参数) 选项参数: 短选项:以 - 开头,单个字母(如 -l 表示长格…...
服务器中CC攻击的特点有哪些?
CC攻击作为一种常见的网络攻击类型,主要是用来攻击网站页面的,当大量的用户在访问网站的过程中,打开页面的速度会变得比较慢,给数据库造成的压力就越大,CC攻击会消耗大量的服务器资源,给企业带来一定的经济…...

WEB3全栈开发——面试专业技能点P1Node.js / Web3.js / Ethers.js
一、Node.js 事件循环 Node.js 的事件循环(Event Loop)是其异步编程的核心机制,它使得 Node.js 可以在单线程中实现非阻塞 I/O 操作。 🔁 简要原理 Node.js 是基于 libuv 实现的,它使用事件循环来处理非阻塞操作。事件…...

从webrtc到janus简介
1.基础知识 1.1 信令的基础知识 在 WebRTC(Web Real-Time Communication) 中,信令(Signaling) 是实现浏览器之间实时通信的关键机制,负责在通信双方(或多方)之间传递控制信息&…...

如何打造一款金融推理工具Financial Reasoning Workflow:WebUI+Ollama+Fin-R1+MCP/RAG
在之前的文章中,我探讨了如何使用具身人工智能,让大语言模型智能体来模仿[当今著名对冲基金经理的投资策略]。 在本文中,我将探讨另一种方法,该方法结合了经过金融推理训练的特定大语言模型(LLM)࿰…...

sql入门语句-案例
Sql入门 数据库、数据表、数据的关系介绍 数据库 用于存储和管理数据的仓库 一个库中可以包含多个数据表 数据表 数据库最重要的组成部分之一 它由纵向的列和横向的行组成(类似excel表格) 可以指定列名、数据类型、约束等 一个表中可以存储多条数据 数据 想要永久化存储…...

Redis Key过期策略
概述 Redis的Key过期策略是其内存管理系统的核心组成部分,主要包括「被动过期」、「主动过期」和「内存淘汰」三个机制。其中「内存淘汰」相关内容已经在上一篇「Redis内存淘汰策略」中进行了详细的讲解,有信兴趣的同学可以在回顾上一篇文章。本文将着重…...

【前端】es6相关,柯里化
0. 严格模式 严格模式的概念从ES6引进。通过严格模式,可以在函数内部选择进行较为严格的全局或局部的错误条件检测。 MDN中严格模式的描述 严格模式通过抛出错误来消除了一些原有静默错误严格模式修复了一些导致 JavaScript引擎难以执行优化的缺陷:有时…...

Linux中su与sudo命令的区别:权限管理的关键差异解析
💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「storms…...
动静态库的使用(Linux)
1.库 通俗来说,库就是现有的,可复用的代码,例如:在C/C语言编译时,就需要依赖相关的C/C标准库。本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。通常我们可以在windows下看到一些后…...