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

记一次线上SQL死锁事故

一、 引言

SQL死锁是一个常见且复杂的并发控制问题。当多个事务在数据库中互相等待对方释放锁时,就会形成死锁,从而导致事务无法继续执行,影响系统的性能和可用性。死锁不仅会导致数据库操作的阻塞,增加延迟,还可能对系统的稳定性和响应速度产生严重影响。因此,了解死锁的成因、识别死锁的方式以及采取有效的防范和解决措施,对于数据库管理员和开发人员而言,具有重要的实践意义。本文将根据一次线上SQL死锁的真实案例刨析发生的机制、如何检测死锁及常见的解决策略,以帮助提高数据库系统的效率和可靠性。

二、MySQL中的锁

在介绍死锁之前,我们先来看看MySQL中的锁有哪些。

2.1 共享锁与排他锁

InnoDB 实现了标准的行级锁,包括两种:共享锁(简称 s 锁)、排它锁(简称 x 锁)。

  • 共享锁(S锁):允许持锁事务读取一行。

  • 排他锁(X锁):允许持锁事务更新或者删除一行。

如果事务 T1 持有行 r 的 s 锁,那么另一个事务 T2 请求 r 的锁时,会做如下处理:

  • T2 请求 s 锁立即被允许,结果 T1 T2 都持有 r 行的 s 锁

  • T2 请求 x 锁不能被立即允许

如果 T1 持有 r 的 x 锁,那么 T2 请求 r 的 x、s 锁都不能被立即允许,T2 必须等待T1释放 x 锁才可以,因为X锁与任何的锁都不兼容。

 

兼容性SX
S兼容不兼容
X不兼容不兼容

 2.2 意向锁

  • 意向共享锁( IS 锁):事务想要获得一张表中某几行的共享锁

  • 意向排他锁( IX 锁): 事务想要获得一张表中某几行的排他锁

比如:事务1在表1上加了S锁后,事务2想要更改某行记录,需要添加IX锁,由于不兼容,所以需要等待S锁释放;如果事务1在表1上加了IS锁,事务2添加的IX锁与IS锁兼容,就可以操作,这就实现了更细粒度的加锁。

兼容性ISIXSX
IS兼容兼容兼容不兼容
IX兼容兼容不兼容不兼容
S兼容不兼容兼容不兼容
X不兼容不兼容不兼容不兼容

2.3 记录锁(Record Locks)

  • 记录锁是最简单的行锁,仅仅锁住一行。如:SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE

  • 记录锁永远都是加在索引上的,即使一个表没有索引,InnoDB也会隐式的创建一个索引,并使用这个索引实施记录锁。

  • 会阻塞其他事务对其插入、更新、删除

 2.4 间隙锁(Gap Locks)

  • 间隙锁是一种加在两个索引之间的锁,或者加在第一个索引之前,或最后一个索引之后的间隙。

  • 使用间隙锁锁住的是一个区间,而不仅仅是这个区间中的每一条数据。

  • 间隙锁只阻止其他事务插入到间隙中,他们不阻止其他事务在同一个间隙上获得间隙锁,所以 gap x lock 和 gap s lock 有相同的作用。

2.5 Next-Key Locks

  • Next-key锁是记录锁和间隙锁的组合,它指的是加在某条记录以及这条记录前面间隙上的锁。

2.6 插入意向锁(Insert Intention)

  • 插入意向锁是在插入一行记录操作之前设置的一种间隙锁,这个锁释放了一种插入方式的信号,亦即多个事务在相同的索引间隙插入时如果不是插入间隙中相同的位置就不需要互相等待。

三、线上SQL死锁的背景

之前自己参与过一个农业类项目,在项目初期,我们是没有将读写表分离的,而是基于一个主库完成读写操作。后面随着业务方的推广业务量逐渐增大,我们偶尔会收到系统的异常报警信息,DBA 通知我们数据库出现了死锁异常。业务开始是比较简单的,就是新增订单、修改订单、查询订单等操作,那为什么会出 现死锁呢?经过日志分析,我们发现是作为幂等性校验的一张表经常出现死锁异常。

接下来我将在本地复现该问题,帮助我们更好的去理解SQL死锁的原因。

首先,创建一张订单记录表,该表主要用于校验订单重复创建:

CREATE TABLE `order_record` (`id` int(11) NOT NULL AUTO_INCREMENT,`order_no` int(11) DEFAULT NULL,`status` int(4) DEFAULT NULL,`create_date` datetime(0) DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE,INDEX `idx_order_status`(`order_no`,`status`) USING BTREE
) ENGINE = InnoDB

为了能重现该问题,我们先将事务设置为手动提交。MySQL 数据库默认情况下是自动提交事务,我们可以通过以 下命令行查看自动提交事务是否开启:

先将 MySQL 数据库的事务提交设置为手动提交,我们可以看见此时已经改为手动提交了。

当时订单在做幂等性校验时,先是通过订单号检查订单是否存在,如果不存在则新增订单记录。 知道具体的逻辑之后,我们再来模拟创建产生死锁的运行 SQL 语句。首先,我们模拟新建两个订单,并按照以下顺序执行幂等性校验 SQL 语句

事务A事务B
BEGIN;BEGIN;
select id from order_record where order_on = 4 for update;  // 检查是否存在order_no = 4的订单
select id from order_record where order_on = 5 for update;  // 检查是否存在order_no = 5的订单

insert into order_record (order_no,status,create_date) values (4,1,'2025-03-20 15:30:25');  // 如果没有则插入信息

此时,锁等待 .......

insert into order_record (order_no,status,create_date) values (5,1,'2025-03-20 15:30:25');  // 如果没有则插入信息

此时,锁等待 .......

commit; (未完成)commit; (未完成)

此时,我们会发现两个事务已经进入死锁状态。我们可以在 information_schema数据库(版本5.7) 中查询到具体的死锁情况,如下图所示:

你可能会想,为什么 SELECT 要加 for update 排他锁,而不是使用共享锁呢?试 想下,如果是两个订单号一样的请求同时进来,就有可能出现幻读。也就是说,一开始事务 A 中的查询没有该订单号,后来事务 B 新增了一个该订单号的记录,此时事务 A 再新增一条该订单号记录,就会创建重复的订单记录。面对这种情况,我们可以使用锁间隙算法来防止幻读。

四、产生死锁的原因

在开始问题之前我们先来回想一下行锁的具体实现算法有那些?行锁的具体实现算法有三种:record lock、gap lock 以及 next-key lock。record lock 是专门对索引项加锁;gap lock 是对索引项之间的间隙加锁;next-key lock 则是前面两 种的组合,对索引项以其之间的间隙加锁。

只在可重复读或以上隔离级别下的特定操作才会取得 gap lock 或 next-key lock,在 Select、Update 和 Delete 时,除了基于唯一索引的查询之外,其它索引查询时都会获取 gap lock 或 next-key lock,即锁住其扫描的范围。主键索引也属于唯一索引,所以主键 索引是不会使用 gap lock 或 next-key lock。 在 MySQL 中,gap lock 默认是开启的,即 innodb_locks_unsafe_for_binlog 参数值是 disable 的,且 MySQL 中默认的是 RR 事务隔离级别。

当我们执行以下查询 SQL 时,由于 order_no 列为非唯一索引,此时又是 RR 事务隔离级 别,所以 SELECT 的加锁类型为 gap lock,这里的 gap 范围是 (4,+∞)。

SELECT id FROM order_record where order_no = 4 for update

执行查询 SQL 语句获取的 gap lock 并不会导致阻塞,而当我们执行以下插入 SQL 时,会 在插入间隙上再次获取插入意向锁。插入意向锁其实也是一种 gap 锁,它与 gap lock 是 冲突的,所以当其它事务持有该间隙的 gap lock 时,需要等待其它事务释放 gap lock 之 后,才能获取到插入意向锁。

以上事务 A 和事务 B 都持有间隙 (4,+∞)的 gap 锁,而接下来的插入操作为了获取到插 入意向锁,都在等待对方事务的 gap 锁释放,于是就造成了循环等待,导致死锁。

insert into order_record (order_no,states,create_time) values (5,1,'2025-03-20 15:30:25'); 

五、避免死锁的措施

知道了死锁问题源自哪儿,就可以找到合适的方法来避免它了。

避免死锁最直观的方法就是在两个事务相互等待时,当一个事务的等待时间超过设置的某一 阈值,就对这个事务进行回滚,另一个事务就可以继续执行了。这种方法简单有效,在 InnoDB 中,参数 innodb_lock_wait_timeout 是用来设置超时时间的。

另外,我们还可以将 order_no 列设置为唯一索引列。虽然不能防止幻读,但我们可以利用 它的唯一性来保证订单记录不重复创建,这种方式唯一的缺点就是当遇到重复创建订单时会抛出异常。

六、预防死锁

解决死锁的最佳方式当然就是预防死锁的发生了,我们平时编程中,可以通过以下一些常规 手段来预防死锁的发生:

1. 在编程中尽量按照固定的顺序来处理数据库记录,假设有两个更新操作,分别更新两条 相同的记录,但更新顺序不一样,有可能导致死锁;

2. 在允许幻读和不可重复读的情况下,尽量使用 RC 事务隔离级别,可以避免 gap lock 导 致的死锁问题;

3. 更新表时,尽量使用主键更新;

4. 避免长事务,尽量将长事务拆解,可以降低与其它事务发生冲突的概率;

5. 设置锁等待超时参数,我们可以通过 innodb_lock_wait_timeout 设置合理的等待超时 阈值,特别是在一些高并发的业务中,我们可以尽量将该值设置得小一些,避免大量事务等 待,占用系统资源,造成严重的性能开销。

七 小结

数据库发生死锁的概率并不是很大,一旦遇到了,就一定要彻查具体原因,尽快找出解决方案,我们只有先对 MySQL 的 InnoDB 存储引擎有足够的了解,才能剖析出造成死锁的具体原因。

相关文章:

记一次线上SQL死锁事故

一、 引言 SQL死锁是一个常见且复杂的并发控制问题。当多个事务在数据库中互相等待对方释放锁时,就会形成死锁,从而导致事务无法继续执行,影响系统的性能和可用性。死锁不仅会导致数据库操作的阻塞,增加延迟,还可能对…...

Java并发编程 什么是分布式锁 跟其他的锁有什么区别 底层原理 实战讲解

目录 一、分布式锁的定义与核心作用 二、分布式锁与普通锁的核心区别 三、分布式锁的底层原理与实现方式 1. 核心实现原理 2. 主流实现方案对比 3. 关键技术细节 四、典型问题与解决方案 五、总结 六、具体代码实现 一、分布式锁的定义与核心作用 分布式锁是一种在分布…...

【react】在react中async/await一般用来实现什么功能

目录 基本概念 工作原理 优点 注意事项 底层原理 实际应用场景 1. 数据获取 (API 请求) 2. 表单提交 3. 异步状态管理 4. 异步路由切换 5. 异步数据预加载 6. 第三方 API 调用 7. 文件上传/下载 8. 路由导航拦截 关键注意事项 基本概念 async 函数:用…...

Axure项目实战:智慧城市APP(六)市民互动(动态面板、显示与隐藏)

亲爱的小伙伴,在您浏览之前,烦请关注一下,在此深表感谢! 课程主题:市民互动 主要内容:动态面板、显示与隐藏交互应用 应用场景:AI产品交互、互动类应用 案例展示: 案例视频&am…...

为何服务器监听异常?

报错: 执行./RCF后出现监听异常--在切换网络后,由于前面没有退出./RCF执行状态;重新连接后,会出现服务器监听异常 原因如下: 由于刚开始登录内网,切换之后再重新登录内网,并且切换网络的过程中…...

1.认识Excel

一 Excel 可以用来做什么 二 提升技巧 1.数据太多 2.计算太累 3.提升数据的价值和意义 4.团队协作 三 学习目标 学习目标不是为了掌握所有的技能,追逐新功能。而是学知识来解决需求,如果之前的技能和新出的技能都可以解决问题,那不学新技能也…...

目标跟踪——deepsort算法详细阐述

deepsort 算法详解 Unmatched Tracks(未匹配的轨迹) 本质角色: 是已存在的轨迹在当前帧中“失联”的状态,即预测位置与检测结果不匹配。 生命周期阶段: 已初始化: 轨迹已存在多帧,可能携带历史信息(如外观特征、运动模型)。 未被观测到: 当前帧中未找到对应的检测框…...

AI Agent 是什么?从 Chatbot 到自动化 Agent(LangChain、AutoGPT、BabyAGI)

1. 引言:AI Agent 的演进 AI Agent(人工智能智能体)是 AI 发展的重要方向之一。早期的 AI 主要以 Chatbot 形式存在,如客服机器人、智能助手等,主要基于 NLP 技术进行任务处理。而随着大模型(LLM)能力的提升,AI Agent 逐步演进为能够自主执行任务的智能体,如 AutoGPT…...

ngx_http_core_root

定义在 src\http\ngx_http_core_module.c static char * ngx_http_core_root(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {ngx_http_core_loc_conf_t *clcf conf;ngx_str_t *value;ngx_int_t alias;ngx_uint_t …...

python康复日记-request库的使用,爬虫自动化测试

一,request的简单应用 #1请求地址 URLhttps://example.com/login #2参数表单 form_data {username: admin,password: secret } #3返回的响应对象response response requests.post(URL,dataform_data,timeout5 ) #4处理返回结果,这里直接打印返回网页的…...

光谱范围与颜色感知的关系

光谱范围与颜色感知是光学、生理学及技术应用交叉的核心课题,两者通过波长分布、人眼响应及技术处理共同决定人类对色彩的认知。以下是其关系的系统解析: ‌1.基础原理:光谱范围与可见光‌ ‌光谱范围定义‌: 电磁波谱中能被特定…...

OpenCV vs MediaPipe:哪种方案更适合实时手势识别?

引言 手势识别是计算机视觉的重要应用,在人机交互(HCI)、增强现实(AR)、虚拟现实(VR)、智能家居控制、游戏等领域有广泛的应用。实现实时手势识别的技术方案主要有基于传统计算机视觉的方法&am…...

el-select下拉框,搜索时,若是匹配后的数据有且只有一条,则当失去焦点时,默认选中该条数据

1、使用指令 当所需功能只能通过直接的 DOM 操作来实现时&#xff0c;才应该使用自定义指令。可使用方法2封装成共用函数&#xff0c;但用指令他人复用时比较便捷。 <el-tablev-loading"tableLoading"border:data"tableList"default-expand-allrow-key…...

网络地址转换技术(2)

NAT的配置方法&#xff1a; &#xff08;一&#xff09;静态NAT的配置方法 进入接口视图配置NAT转换规则 Nat static global 公网地址 inside 私网地址 内网终端PC2&#xff08;192.168.20.2/24&#xff09;与公网路由器AR1的G0/0/1&#xff08;11.22.33.1/24&#xff09;做…...

Python正则表达式(一)

目录 一、正则表达式的基本概念 1、基本概念 2、正则表达式的特殊字符 二、范围符号和量词 1、范围符号 2、匹配汉字 3、量词 三、正则表达式函数 1、使用正则表达式&#xff1a; 2、re.match()函数 3、re.search()函数 4、findall()函数 5、re.finditer()函数 6…...

【TI MSPM0】PWM学习

一、样例展示 #include "ti_msp_dl_config.h"int main(void) {SYSCFG_DL_init();DL_TimerG_startCounter(PWM_0_INST);while (1) {__WFI();} } TimerG0输出一对边缘对齐的PWM信号 TimerG0会输出一对62.5Hz的边缘对齐的PWM信号在PA12和PA13引脚上&#xff0c;PA12被…...

MySQL: 创建两个关联的表,用联表sql创建一个新表

MySQL: 创建两个关联的表 建表思路 USERS 表&#xff1a;包含用户的基本信息&#xff0c;像 ID、NAME、EMAIL 等。v_card 表&#xff1a;存有虚拟卡的相关信息&#xff0c;如 type 和 amount。关联字段&#xff1a;USERS 表的 V_CARD 字段和 v_card 表的 v_card 字段用于建立…...

更改 vscode ! + table 默认生成的 html 初始化模板

vscode ! 快速成的 html 代码默认为&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>D…...

使用LVS的 NAT 模式实现 3 台RS的轮询访问

节点规划 1、配置RS RS的网络配置为NAT模式&#xff0c;三台RS的网关配置为192.168.10.8 1.1配置RS1 1.1.1修改主机名和IP地址 [rootlocalhost ~]# hostnamectl hostname rs1 [rootlocalhost ~]# nmcli c modify ens160 ipv4.method manual ipv4.addresses 192.168.10.7/24…...

R 基础语法

R 基础语法 引言 R 是一种针对统计计算和图形表示而设计的编程语言和环境。它广泛应用于统计学、生物信息学、数据挖掘等领域。本文将为您介绍 R 语言的基础语法,帮助您快速上手。 R 的基本结构 R 语言的基本结构包括:变量、数据类型、运算符、控制结构、函数等。 变量 …...

MySQL实战(尚硅谷)

要求 代码 # 准备数据 CREATE DATABASE IF NOT EXISTS company;USE company;CREATE TABLE IF NOT EXISTS employees(employee_id INT PRIMARY KEY,first_name VARCHAR(50),last_name VARCHAR(50),department_id INT );DESC employees;CREATE TABLE IF NOT EXISTS departments…...

华为p10 plus 鸿蒙2.0降级emui9.1.0.228

需要用到的工具 HiSuite Proxy V3 华为手机助手11.0.0.530_ove或者11.0.0.630_ove应该都可以。 官方的通道已关闭&#xff0c;所以要用代理&#xff0c;127.0.0.1端口7777 https://www.firmfinder.ml/ https://professorjtj.github.io/v2/ https://hisubway.online/articl…...

C# Modbus RTU学习记录

继C# Modbus TCP/IP学习记录后&#xff0c;尝试串口通信。 操作步骤&#xff1a; 1.使用Visual Studio安装Nuget包NModbus.Serial。 2.使用Modbus Slave应用程序&#xff0c;工具栏Connection项&#xff0c;单击Connect&#xff0c;弹窗Connection Setup&#xff0c;修改Con…...

AI+Xmind自动生成测试用例(思维导图格式)

一、操作步骤: 步骤1:创建自动生成测试用例智能体 方式:使用通义千问/豆包智能体生成,以下两个是我已经训练好的智能体,直接打开使用即可 通义智能体: https://lxblog.com/qianwen/share?shareId=b0cd664d-5001-42f0-b494-adc98934aba5&type=agentCard 豆包智能…...

单片机 - 位运算详解(``、`|`、`~`、`^`、`>>`、`<<`)

单片机中的位运算详解&#xff08;&、|、~、^、>>、<<&#xff09; 位运算是单片机编程&#xff08;C/C&#xff09;中经常使用的技巧&#xff0c;用于高效地操作寄存器、I/O 端口和数据。以下是各位运算符的详细解析&#xff0c;并结合单片机实际应用举例。 …...

chrome插件开发之API解析-chrome.tabs.query

chrome.tabs.query 是 Chrome 扩展开发中用于查询浏览器标签页信息的 API。它允许你根据指定的条件获取当前浏览器中所有匹配的标签页。这个 API 返回一个 Promise&#xff0c;解析后会得到一个包含匹配标签页信息的数组。 常见用途 获取当前活动标签页&#xff1a;可以获取当…...

(二)手眼标定——概述+原理+常用方法汇总+代码实战(C++)

一、手眼标定简述 手眼标定的目的&#xff1a;让机械臂和相机关联&#xff0c;相机充当机械臂的”眼睛“&#xff0c;最终实现指哪打哪 相机的使用前提首先需要进行相机标定&#xff0c;可以参考博文&#xff1a;&#xff08;一&#xff09;相机标定——四大坐标系的介绍、对…...

3D点云的深度学习网络分类(按照作用分类)

1. 3D目标检测&#xff08;Object Detection&#xff09; 用于在点云中识别和定位目标&#xff0c;输出3D边界框&#xff08;Bounding Box&#xff09;。 &#x1f539; 方法类别&#xff1a; 单阶段&#xff08;Single-stage&#xff09;&#xff1a;直接预测3D目标位置&am…...

【Linux网络-NAT、代理服务、内网穿透】

一、NAT技术 1.NAT技术背景 之前我们讨论了&#xff0c;IPV4协议中&#xff0c;IP地址数量不充足的问题 NAT技术当前解决IP地址不够用的主要手段&#xff0c;是路由器的一个重要功能 NAT&#xff08;网络地址转换&#xff0c;Network Address Translation&#xff09;是一种…...

Windows 和 Linux 操作系统架构对比以及交叉编译

操作系统与架构兼容性详解 1. 可执行文件格式&#xff1a;PE vs ELF Windows: PE (Portable Executable) 格式 详细解释&#xff1a; PE 格式是 Windows 下的可执行文件标准 包含多个区段&#xff08;Sections&#xff09;&#xff0c;如代码段、数据段、资源段 文件头包含…...