[MySQL]-死锁案例-唯一索引上的并发插入

news2025/2/25 15:56:42

[MySQL]-死锁案例-唯一索引上的并发插入

森格 | 2022年12月

本文是对实际work中遇到的死锁问题的复现,其目的是学会去分析死锁日志、还原日志上下文、理解死锁产生原因、MySQL处理机制(回滚事务的选择),最后到死锁的解决方案的提出。


一、死锁是什么

1.1 定义

死锁,是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等的进程称为死锁进程。

1.2 必要条件

  • 互斥条件:指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放
  • 请求和保持条件:指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放
  • 不可剥夺条件:指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放 。
  • 循环等待条件:指在发生死锁时,必然存在一个进程——资源的环形链。

这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。


二、死锁场景复现

对于死锁的学习,个人认为最好的方式就是自己去验证,通过复现问题分析整个过程,从而提出解决方案。

本案例是由于在唯一索引高并发插入导致的死锁。

2.1 建表

mysql>CREATE TABLE t (
id BIGINT(20) NOT NULL AUTO_INCREMENT,
a BIGINT(20) NOT NULL,
b BIGINT(20) NOT NULL,
name VARCHAR(255) NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY ind_a_b (a,b)
) ENGINE=INNODB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4;

结果如下图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x6BYX05u-1671607367226)(C:\Users\PC\AppData\Roaming\Typora\typora-user-images\image-20221220140332614.png)]

2.2 场景模拟及锁分析

注:事务隔离级别为RC。

2.2.1 场景验证一

事务1插入后回滚,事务2,3一个插入成功,一个产生死锁。

Session ASession BSessionC备注
T1set autocommit = 0;[set autocommit = 0;][set autocommit = 0;]
T2INSERT INTO t(a,b,NAME) VALUES(7,1,‘name’);
Query OK, 1 row affected (0.00 sec)
执行成功,此A具有隐式锁,因为当前事务A加的锁,不可能产生锁冲突。
T3INSERT INTO t(a,b,NAME) VALUES(7,1,‘name’);等待产生锁冲突,事务A的锁升为X锁,事务B等待S锁
T4INSERT INTO t(a,b,NAME) VALUES(7,1,‘name’);等待事务C也等待S锁
T5rollback;事务A回滚,X锁释放,事务B和C同时拿到S锁
T6Query OK, 1 row affected (36.41 sec)ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction事务B,事务C各自持有S锁,当前间隙事务A仍在活跃事务数组m_ids中,所以事务B和C同时想获取插入意向锁X锁,两锁互斥,导致死锁。事务C报死锁错误后释放S锁,另外一个插入成功。

2.2.2 补充

1、共享锁、排它锁、意向锁的兼容矩阵如下

XIXSIS
X冲突冲突冲突冲突
IX冲突兼容冲突兼容
S冲突冲突兼容兼容
IS冲突兼容兼容兼容

2、insert的加锁过程

根据官方文档指出

INSERT对插入的行设置独占锁。此锁是索引记录锁,而不是下一个密钥锁(即,没有间隙锁),并且不会阻止其他会话在插入的行之前插入到间隙中。

在插入行之前,将设置一种称为插入意图间隙锁的间隙锁。该锁表示插入的意图,即插入到同一索引间隙中的多个事务如果不在间隙中的同一位置插入,则无需彼此等待。假设存在值为4和7的索引记录。尝试插入值为5和6的单独事务在获得插入行的排他锁之前,都会使用插入意图锁锁定4和7之间的间隙,但不会彼此阻止,因为这些行不冲突。

如果发生重复密钥错误,将设置重复索引记录的共享锁。如果有多个会话试图插入同一行(如果另一个会话已经具有独占锁),则使用共享锁可能会导致死锁。如果另一个会话删除了该行,则可能会发生这种情况。

3、加锁顺序

不知道有没有人对事务B、C的 autocommit 设置有疑问,为什么事务的手动提交的与否都可以看到死锁?

解答:

我们来分析一下,事务A、B、C进行事务等待队列,事务A先对插入记录的X锁(唯一索引),此时事务B、C等待插入记录的S锁(唯一索引);A回滚释放X锁(唯一索引),此时B、C拿到插入记录的S锁,此时两事务都想加IX锁,到这里就会出现死锁了,截止这里我们可以发现,死锁是出现在加唯一索引加锁的过程,还并未到达对主键加锁的阶段,所以事务B、C是否设置自动提交与我们是否能看到死锁并无太大关系。

2.2.3 场景验证二

事务1正常提交,事务2,3插入失败,没有产生死锁。

时间\会话Session ASession BSession C备注
T1set autocommit = 0;
T2INSERT INTO t(a,b,NAME) VALUES(7,1,‘name’);
Query OK, 1 row affected (0.00 sec)
执行成功,此A具有隐式锁,因为当前事务A加的锁,不可能产生锁冲突。
T3INSERT INTO t(a,b,NAME) VALUES(7,1,‘name’);等待产生锁冲突,事务A的锁升为X锁,事务B等待S锁
T4INSERT INTO t(a,b,NAME) VALUES(7,1,‘name’);等待事务C也等待S锁
T5commit;事务A提交,事务B和C获得S锁,当前插入间隙无活跃事务,进行唯一性约束检查
T6ERROR 1062 (23000): Duplicate entry ‘7-1’ for key ‘ind_a_b’ERROR 1062 (23000): Duplicate entry ‘7-1’ for key ‘ind_a_b’唯一性约束检查后发现唯一冲突,结束

三、事务回滚代价

3.1 查看死锁日志

mysql> show engine innodb status;

在这里插入图片描述

3.2 事务信息

在innodb中,有三张表可以帮助我们更好去分析死锁信息:

  • information_schema.innodb_trx:事务信息表。
  • information_schema.innodb_locks:事务锁的信息表。
  • information_schema.innodb_lock_waits:锁等待关系表。

下图是场景复现时所截information_schema.innodb_trx表信息:

在这里插入图片描述

3.2 事务优先级

MySQL选择了回滚事务2(trx_id 为31205716),对于MySQL来说,凡事都会考虑一个代价,在解除死锁方面,会选择回滚事务产生影响最小的一个进行回滚。

MySQL8.0.20之前在等待锁的事务优先级排序采取FIFO算法,之后采取CATS算法。该算法通过分配调度权限对等待的事务进行优先级排序,该权重是根据事务阻塞的事务数量计算的。例如,两个事务正在等待同一对象上的锁,那么阻塞最多事务的事务将被分配更大的调度权重,如果权重相等,则优先考虑等待时间最长的事务。


四、解决方案

对于实际场景下的在唯一键值进行高并发插入的死锁,其解决方案之一为:当前事务在执行插入操作之前为其记录申请排它锁

SELECT id FROM t WHERE a=7 AND b=1 FOR UPDATE;
INSERT INTO t(a,b,NAME) VALUES(7,1,'name');

如此,可以防止其他并发事务对该记录加S锁,从而避免拥有S锁再去申请X锁造成死锁。

缺点:

  • 增加一条查询语句,在数据库的层面上会增加QPS。
  • 但更为重要的是,会使得事务串行等待,增加时间成本。

优点:

  • 实现程度上,与其从业务层解决问题,这种方案显得十分简单可行。
  • 成本消耗上,从业务层解决这种小概率问题带来的成本增加来说,该方案就显得九两拨千金了。

五、总结

通过对本次死锁的学习,从死锁复现,事务的回滚与提交,MySQL事务优先级,事务锁的分析等等中,更需要自己在处理问题中要:

  • 要找到问题的原因所在,解决自己的盲区
  • 问题的复现也要做到准确,去寻找技巧
  • 问题的解决需要真正做到可行,但同时也需要理清方案的优缺点

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

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

相关文章

7张图,剖析Redis缓存,文末附Redis工具类源码,建议收藏

一、缓存是什么? 缓存就是数据交换的缓存区,是存储数据的地方,一般读写性能较高。 二、缓存的作用和成本 1、缓存的作用 降低后端负载 提高读写效率,降低响应时间 2、缓存的成本 数据一致性成本 代码维护成本 运维成本 三、…

IDEA中新建找不到Vue Component | IDEA右键Create New Servlet找不到Setvlet

💗wei_shuo的个人主页 💫wei_shuo的学习社区 🌐Hello World ! 解决:IDEA中新建找不到Vue Component 打开IDEA,依次打开 (文件——设置——编辑器——文件或代码模板) 找到右侧&#…

Spring—Spring AOP1

文章目录AOP概念的引入AOP相关的概念1.AOP的概述2. AOP的优势3. AOP的底层原理如何利用AOP对原有业务进行增强利用注解方式切入点表达式AOP相关的术语————————————————————————————————AOP概念的引入 首先我们来看一下登录的原理 如上图所示这…

【云原生 | Kubernetes 实战】16、K8s 配置管理中心 ConfigMap 实现微服务配置管理

目录 一、ConfigMap 概述 1.1 什么是 ConfigMap? 1.2 ConfigMap 能解决哪些问题? 1.3 ConfigMap 应用场景 1.4 局限性 二、ConfigMap 创建方法 2.1 根据字面值创建 ConfigMap 2.2 基于文件创建 ConfigMap 2.3 基于目录创建 ConfigMap 2.4 编…

【大数据系列之MySQL】(二十二):MySQL中的分组查询group by

对于常见的函数都是单行函数,说白了就是一一映射,输入一个值则输出对应的值,但是MySQL中还存在聚合函数就是输入一组值则返回一个值,常见的例如:sum、max等 很多时候需要对数据中的某些字段进行分组,探究每…

%27 CORS 跨域资源共享

1、CORS (跨域资源共享) 由一系列的 HTTP 响应头组成,这些响应头可以决定浏览器是否阻止前端 js 代码跨域获取资源 2、CORS 的响应头 (1)、Access-Control-Allow-Origin res.setHeader(‘Access-Control-Allow-Origin’…

web:常见安全问题

一、XSS XSS(Cross-Site Scripting),跨站脚本攻击,因为缩写和css重叠,所以只能叫XSS。跨站脚本攻击是指通过存在安全漏洞的web网站注册用户的浏览器内运行非法的HTML标签或JavaScript进行的一种攻击。 跨站脚本攻击有可能造成一下影响&#…

记一次赤裸裸的教训:All elements are null

wshanshi:记一次赤裸裸的教训…All elements are null… 一、异常信息 数据库查询统计相关业务,未使用分组group by,仅单独使用聚合函数。如下图所示,使用了SUM()函数。 假如数据库中未匹配到相关数据,结果集用List接…

希尔伯特-包络分析步骤与实例

希尔伯特-包络分析流程 对于齿轮箱振动信号而言,由于存在多对齿轮同时参与啮合,那么,测量得到的信号将可能出现多个以齿轮啮合频率或及谐频为载波频率、轴频为调制频率的幅值调制、频率调制或混合调制的情况,除此之外&#xff…

logback+slf4j日志详解

前言 项目中日志系统是必不可少的,目前比较流行的日志框架有log4j、logback等,可能大家还不知道,这两个框架的作者是同一个人,Logback旨在作为流行的log4j项目的后续版本,从而恢复log4j离开的位置。 另外 slf4j(Simp…

第二证券|昨日涨停,今日1分钟闪崩跌停,超1亿资金排队“出逃”!

养老概念股悦心健康(SZ002162)在接连2个涨停后,12月21日早盘,悦心健康大幅低开,1分钟闪崩跌停。 值得注意的是,20日盘后龙虎榜数据显现,万和证券股份有限公司成都通盈街证券营业部净买入1492.92…

HttpUnit是什么?如何应用?

推荐阅读: [内部资源] 想拿年薪30W的软件测试人员,这份资料必须领取~ Python自动化测试全栈性能测试全栈,挑战年薪40W 什么是HttpUnit? HttpUnit是基于JUnit构建的一个开源的测试框架,专门针对Web应用的测试,用于解…

TM32 DMA1和DMA2通道一览表、STM32F103C8T6定时器通道对应的引脚

TIM1_BRK_IRQn 24, TIM1_UP_IRQn 25, TIM1_TRG_COM_IRQn 26, TIM1_CC_IRQn 27, TIM2_IRQn 28, TIM3_IRQn 29, 这个函数TIM_SetCompare1&#xff0c…

计算机毕设Python+Vue学习互助平台网站(程序+LW+部署)

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 Ma…

Redhat rpm常用命令以及如何配置yum软件仓库使用yum install

yum 软件仓库配置教程一、RPM(红帽软件包管理器)二、Yum 软件仓库简介三、注册Red Hat开发者四、配置 Yum 软件仓库一、RPM(红帽软件包管理器) 在 RPM(红帽软件包管理器)公布之前,要想在 Linux…

如何用 Python 在 Excel 中画柱状图

我们手动在 Excel 表格中画柱状图是很简单的事情,但是一旦这种简单的工作需要每天都做,那么最好的办法就是用 Python 来自动完成。 今天分享一招,如何用 Python 在 Excel 中画柱状图。 这里借助于工具 openpyxl,如果有更好的工具…

2. 【gRPC系列学习】 创建一元gRPC的客户端与服务端

学习讲求循序渐进,在分析代码原理之前应该熟练使用,本节我们一起搭建最简单一元gRPC模式,其中也包含安装protoc工具。 1. 创建项目目录结构 pb文件夹用于存放proto文件以及生成的pb文件 client文件夹存放客户端代码 server文件夹存放服务端代码 现在并未创建这几个文件,里面…

更懂城市、更懂人:闪马智能再添双认证

在中国工程院院刊《Engineering》刚刚发布的“2022全球十大工程成就”中,北斗卫星导航系统、嫦娥探月工程以及新冠病毒疫苗研发应用等重大创新,无不显现出人类推动科技发展边界突破的决心和勇气,为人类文明进步提供不竭动力。 眼下&#xff…

Pegasus Serial Port Tool @ Simplicity Version 串口测试工具简化版发布

Pegasus Serial Port Tool Simplicity Version 串口测试工具简化版发布 基于Electron桌面软件开发平台制作的PSPT ( Pegasus Serial Port Tool ) 串口测试工具发布简化版。免费用于任何个人和商业环境使用。 平台:Windows 11 / Windows 10 / Windows 7 介绍&…

设计模式原则 - 迪米特法则(六)

迪米特法则一 官方定义基本介绍二 案例演示普通实现方式案例分析迪米特法则方式三 注意事项一 官方定义 迪米特法则(Law of Demeter, LoD)是1987年秋天由lan holland在美国东北大学一个叫做迪米特的项目设计提出的,它要求一个对象应该对其他对…