MySQL - 死锁的产生及解决方案

article/2025/9/28 4:27:57

MySQL - 死锁的产生及解决方案

  • 1. 死锁与产生死锁的四个必要条件
    • 1.1 什么是死锁
    • 1.2 死锁产生的4个必要条件
  • 2. 死锁案例
    • 2.1 表锁死锁
    • 2.2 行锁死锁
    • 2.3 共享锁转换为排他锁
  • 3. 死锁排查
  • 4. 实例分析
    • 4.1 案例描述
    • 4.2 案例死锁问题复现
    • 4.3 死锁排查
    • 4.4 解决死锁
  • 5. 如何避免死锁

1. 死锁与产生死锁的四个必要条件

1.1 什么是死锁

死锁是指2+进程执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。

摘自:@百度百科
在这里插入图片描述

1.2 死锁产生的4个必要条件

虽然进程在运行过程中,可能发生死锁,但死锁的发生也必须具备一定的条件,死锁的发生必须具备以下四个必要条件:

1)互斥条件: 指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。

2)请求和保持条件: 指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。

3)不剥夺条件: 指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。

4)环路等待条件: 指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源。

我们强调所有四个条件必须同时成立才会出现死锁。环路等待条件意味着占有并等待条件,这样四个条件并不完全独立。

死锁的关键在于:2+ 的 session 加锁的顺序不一致。

那么对应的解决死锁问题的关键就是:让不同的 session 加锁有次序。

2. 死锁案例

2.1 表锁死锁

  • 产生原因:

用户A访问表A(锁住了表A),然后又访问表B;另一个用户B访问表B(锁住了表B),然后企图访问表A;这时用户A由于用户B已经锁住表B,它必须等待用户B释放表B才能继续,同样用户B要等用户A释放表A才能继续,这就死锁就产生了。

用户A–》A表(表锁)–》B表(表锁)
用户B–》B表(表锁)–》A表(表锁)

  • 解决方案:

这种死锁比较常见,是由于程序的 BUG 产生的,除了调整的程序的逻辑没有其它的办法。仔细分析程序的逻辑,对于数据库的多表操作时,尽量按照相同的顺序进行处理,尽量避免同时锁定两个资源,如操作A和B两张表时,总是按先A后B的顺序处理, 必须同时锁定两个资源时,要保证在任何时刻都应该按照相同的顺序来锁定资源。

2.2 行锁死锁

  • 产生原因1:

如果在事务中执行了一条没有索引条件的查询,引发全表扫描,把行级锁上升为全表记录锁定(等价于表级锁),多个这样的事务执行后,就很容易产生死锁和阻塞,最终应用系统会越来越慢,发生阻塞或死锁。

  • 解决方案1:

SQL 语句中不要使用太复杂的关联多表的查询;使用 explain “执行计划"对 SQL 语句进行分析,对于有全表扫描和全表锁定的 SQL 语句,建立相应的索引进行优化。

  • 产生原因2:

两个事务分别想拿到对方持有的锁,互相等待,于是产生死锁。

在这里插入图片描述

  • 解决方案2:

(1)在同一个事务中,尽可能做到一次锁定所需要的所有资源;
(2)按照 id 对资源排序,然后按顺序进行处理。

2.3 共享锁转换为排他锁

  • 产生原因:

事务A 查询一条纪录,然后更新该条纪录;此时事务B 也更新该条纪录,这时事务B 的排他锁由于事务A 有共享锁,必须等A 释放共享锁后才可以获取,只能排队等待。事务A 再执行更新操作时,此处发生死锁,因为事务A 需要排他锁来做更新操作。但是,无法授予该锁请求,因为事务B 已经有一个排他锁请求,并且正在等待事务A 释放其共享锁。

事务A:

-- 共享锁,1
select * from dept where deptno=1 lock in share mode;
-- 排他锁,3
update dept set dname='java' where deptno=1;

事务B:

-- 由于1有共享锁,没法获取排他锁,需等待,2
update dept set dname='Java' where deptno=1;
  • 解决方案:

(1)对于按钮等控件,点击立刻失效,不让用户重复点击,避免引发同时对同一条记录多次操作;

(2)使用乐观锁进行控制。乐观锁机制避免了长事务中的数据库加锁开销,大大提升了大并发量下的系统性能。需要注意的是,由于乐观锁机制是在我们的系统中实现,来自外部系统的用户更新操作不受我们系统的控制,因此可能会造成脏数据被更新到数据库中。

3. 死锁排查

MySQL 提供了几个与锁有关的参数和命令,可以辅助我们优化锁操作,减少死锁发生。

  1. 查看死锁日志:

通过 show engine innodb status \G 命令查看近期死锁日志信息,主要关注日志中的 LATEST DETECTED DEADLOCK 部分;

使用方法:

(1)查看近期死锁日志信息;
(2)使用 explain 查看下 SQL 执行计划。

  1. 查看锁状态变量

通过 show status like 'innodb_row_lock%' 命令检查状态变量,分析系统中的行锁的争夺情况

  • Innodb_row_lock_current_waits:当前正在等待锁的数量
  • Innodb_row_lock_time:从系统启动到现在锁定总时间长度
  • Innodb_row_lock_time_avg: 每次等待锁的平均时间
  • Innodb_row_lock_time_max:从系统启动到现在等待最长的一次锁的时间
  • Innodb_row_lock_waits:系统启动后到现在总共等待的次数

如果等待次数高,而且每次等待时间长,需要分析系统中为什么会有如此多的等待,然后着手定制优化。

4. 实例分析

4.1 案例描述

本次发生死锁的是库存扣减接口,该接口的主要逻辑是用户下单后,扣减订单商品在某个仓库的库存量。比如用户一个在vivo官网下单买了1台X50手机和1台X30耳机,那么下单后,首先根据用户收货地址确定发货仓库,然后从该仓库里面分别减去一个X50库存和一个X30库存。分析死锁sql之前,先看下商品库存表的定义(为方便理解,只保留主要字段):

CREATE TABLE `store` (`id` int(10) AUTO_INCREMENT COMMENT '主键',`sku_code` varchar(45) COMMENT '商品编码',`ws_code` varchar(32) COMMENT '仓库编码',`store` int(10) COMMENT '库存量',PRIMARY KEY (`id`),KEY `idx_skucode` (`sku_code`),KEY `idx_wscode` (`ws_code`)) ENGINE=InnoDB COMMENT='商品库存表'

注意这里分别给 sku_code 和 ws_code 两个字段单独定义了索引:idx_skucode,idx_wscode。这样做的原因主要是业务上有根据单个字段查询的要求。

4.2 案例死锁问题复现

再看下库存扣减update语句:

update store
set store = store-#{store}
where sku_cod e= #{skuCode} and ws_code = #{wsCode} and (store-#{store}) >= 0;

这个 SQL 的业务含义就是对某个商品(skuCode)从某个仓库(wsCode)中扣减 store 个库存量,同时上面的 where 条件同时出现了 sku_code 和 ws_code 字段,压测数据中 sku_code 的选择度要比 ws_code 高,理论上这条 SQL 应该会走 idx_skucode 索引,那么真实情况是怎样的呢?

好,接下来对库存扣减接口卡进行压测,50的并发,每个订单5个商品,刚压不到半分钟就出现了死锁,再压,问题依旧,说明是必现的问题,必现解决后才能继续。

4.3 死锁排查

通过 show engine innodb status \G 命令查看近期死锁日志信息,主要关注日志中的 LATEST DETECTED DEADLOCK 部分:

-----------------------LATEST DETECTED DEADLOCK------------------------2020-xx-xx 21:09:05 7f9b22008700*** (1) TRANSACTION:TRANSACTION 4219870943, ACTIVE 0 sec fetching rowsmysql tables in use 3, locked 3LOCK WAIT 10 lock struct(s), heap size 2936, 3 row lock(s)MySQL thread id 301903552, OS thread handle 0x7f9b21a7b700, query id 5373393954 10.101.22.135 root updatingupdate storeset update_time = now(), store = store-1where sku_code='5468754' and ws_code = 'NO_001' and (store-1) >= 0*** (1) WAITING FOR THIS LOCK TO BE GRANTED:RECORD LOCKS space id 3331 page no 16 n bits 904 index `idx_wscode` of table `store` trx id 4219870943 lock_mode X locks rec but not gap waitingRecord lock, heap no 415 PHYSICAL RECORD: n_fields 2; compact format; info bits 00: len 5; hex 5730303735; asc NO_001;;1: len 8; hex 00000000000025a7; asc % ;;*** (2) TRANSACTION:TRANSACTION 4219870941, ACTIVE 0 sec fetching rows, thread declared inside InnoDB 1mysql tables in use 3, locked 39 lock struct(s), heap size 2936, 4 row lock(s)MySQL thread id 301939956, OS thread handle 0x7f9b22008700, query id 5373393941 10.101.22.135 root updatingupdate storeset update_time = now(), store = store-1where sku_code='5655620' and ws_code = 'NO_001' and (store-1) >= 0*** (2) HOLDS THE LOCK(S):RECORD LOCKS space id 3331 page no 16 n bits 904 index `idx_wscode` of table `store` trx id 4219870941 lock_mode X locks rec but not gapRecord lock, heap no 415 PHYSICAL RECORD: n_fields 2; compact format; info bits 00: len 5; hex 5730303735; asc NO_001;;1: len 8; hex 00000000000025a7; asc % ;;*** (2) WAITING FOR THIS LOCK TO BE GRANTED:RECORD LOCKS space id 3331 page no 7 n bits 328 index `PRIMARY` of table `store` trx id 4219870941 lock_mode X locks rec but not gap waitingRecord lock, heap no 72 PHYSICAL RECORD: n_fields 9; compact format; info bits 00: len 8; hex 00000000000025a7; asc % ;;1: len 6; hex 0000fb85fdf7; asc ;;2: len 7; hex 1a00001d3b21d4; asc ;! ;;3: len 7; hex 35343638373534; asc 5468754;;4: len 5; hex 5730303735; asc NO_001;;5: len 8; hex 8000000000018690; asc ;;6: len 5; hex 99a76b2b97; asc k+ ;;7: len 5; hex 99a7e35244; asc RD;;8: len 1; hex 01; asc ;;

从上面日志可以看出,存在两个事务,分别在执行这两条sql时发生了死锁:

update store set update_time = now(), store = store-1 where sku_code='5468754' and ws_code = 'NO_001' and (store-1) >= 0;update store set update_time = now(), store = store-1 where sku_code='5655620' and ws_code = 'NO_001' and (store-1) >= 0;

看一下实际数据:

在这里插入图片描述
就是说,这两个事务在更新同一张表的不同行时发生了死锁。在我们直观印象里,innodb使用的是行锁,不同的行锁之间应该是互不干扰的?那这是怎么一回事呢?
在这里插入图片描述
和我们想象的不同,InnoDB 既没有使用 idx_skucode 索引,也没有使用 idx_wscode 索引,而是使用了 index_mergeindex_merge 和这两个索引是什么关系呢?

查询资料得知 index_merge 是 MySQL 5.1 后引入的一项索引合并优化技术,它允许对同一个表同时使用多个索引进行查询,并对多个索引的查询结果进行合并(取交集(intersect)、并集(union)等)后返回。

回到上面的 update 语句:where sku_code='5468754' and ws_code = 'NO_001' ;如果没有 index_merge,要么走 idx_skucode 索引,要么走 idx_wscode 索引,不会出现两个索引一起使用的情况。而在使用 index_merge 技术后,会同时执行两个索引,分别查到结果后再进行合并(where条件是and,所以会做交集运算)。再结合第二部分对加锁机制(分步按记录加锁)的理解,是否隐约觉得两个索引的同时加锁是导致死锁的原因呢?

我们再深入死锁日志看一下,日志比较复杂,翻译过来大意如下:

  • 事务一 4219870943 在执行update语句时,在等待索引idx_wscode上的行锁(编号space id 3331 page
    no 16 n bits 904 )。
  • 事务二 4219870941 在执行update语句时,已经持有idx_wscode上的行锁(编号space id 3331 page
    no 16 n bits 904 ),从锁编号来看,就是事务一需要的锁。
  • 事务二 4219870941 同时也在等待主键索引上的一把锁,这把锁谁在持有呢?从这行日志(3: len 7; hex 35343638373534; asc 5468754;;)可以看出,正是事务一要更新的那行记录,说明这把锁被事务一霸占着。

好了,死锁条件已经很清楚了:事务一在等待事务二持有的索引 idx_wscode 上的行锁(编号space id 3331 page no 16 n bits 904 ),而事务二同时也在等待事务一持有的主键索引(5468754)上的锁,大家互不相让,只能僵在那里死锁。

用一张图来说明一下这个情况:

在这里插入图片描述

上图描述的只是发生死锁的一条可能路径,实际上仔细梳理的话还有其他路径也会导致死锁,大家感兴趣可以自己探索。上图解释如下:

  • 事务一(where sku_code=‘5468754’ and ws_code = ‘NO_001’)首先走idx_skucode索引,分别对二级索引和主键索引加锁成功(1-1和1-2);

  • 此时事务二开始执行( where sku_code=‘5655620’ and ws_code = ‘NO_001’),首先也是走idx_skucode(左上)索引,因为和事务一所加锁的记录不冲突,所以也顺利加锁成功(2-1和2-2);

  • 事务二继续执行,这时走的是idx_wscode(右上)索引,先对二级索引加锁成功(2-3,此时事务一还没有开始在idx_wscode上加锁),但是在对主键索引加索引时,发现id=9639的主键索引已经被事务一上锁,因此只能等待(2-4),同时在2-4完成加锁前,对其他记录的加锁也会暂停(2-5和2-6,因为InnoDB是逐条记录加锁的,前一条未完成则后面的不会执行);

  • 此时事务一继续执行,这时走的是idx_wscode索引,但是加锁的时候发现(NO_001,9639)这条索引项已经被事务二上锁,所以也只能等待。同理,后面的1-4也无法执行。

到此就出现了“两个事务,反向加锁"导致的死锁现象。

4.4 解决死锁

死锁的本质原因还是由加锁顺序不同所导致,本例中是由于Index Merge同时使用2个索引方向加锁所导致,解决方法也比较简单,就是消除因index merge带来的多个索引同时执行的情况。

1)利用 force index(idx_skucode) 强制走某个索引,这样 InnoDB 就会忽略index merge,避免多个索引同时加锁的情况。
在这里插入图片描述

2)禁用 Index Merge,这样 InnoDB 只会使用 idx_skucode 和 idx_wscode 中的一个,所有事物加锁顺序都一样,不会造成死锁。

用命令禁用Index Merge:

SET GLOBAL  optimizer_switch='index_merge=off, index_merge_union=off, index_merge_sort_union=off, index_merge_intersection=off';

在这里插入图片描述
重新登录终端后再看下执行计划:
在这里插入图片描述

3)既然Index Merge同时使用了2个独立索引,我们不妨新建一个包含这两个索引所有字段的联合索引,这样InnoDB就只会走这个单独的联合索引,这其实和禁用index merge是一个道理。

新增联合索引:

alter table store add index idx_skucode_wscode(sku_code, ws_code);

再看下执行计划,type = range 说明没有使用 index merge,另外 key = idx_skucode_wscode 说明走的是刚刚创建的联合索引:
在这里插入图片描述

4)最后推荐另外一种绕过 index merge 限制的方式。即去除死锁产生的条件,具体方法是先利用 idx_skucode 和 idx_wscode 查询到主键 id,再拿主键 id 进行 update 操作。这种方式避免了由 update 引入X锁,由于最终更新的条件是唯一固定的,所以不存在加锁顺序的问题,避免了死锁的产生。

案例来源:@dbaplus社群

5. 如何避免死锁

  • 事务尽可能小,不要将复杂逻辑放进一个事务里。
  • 涉及多行记录时,约定不同事务以相同顺序访问。
  • 业务中要及时提交或者回滚事务,可减少死锁产生的概率。
  • 表要有合适的索引。
  • 可尝试将隔离级别改为 RC 。

http://chatgpt.dhexx.cn/article/hk4jRuDN.shtml

相关文章

MySql 死锁

MySql 死锁 一、什么是死锁InnoDB存储引擎定义了四种类型的行锁隔离等级对加锁的影响当前数据对加锁的影响 二、为什么会形成死锁两阶段锁协议产生死锁的四个必要条件 三、MySQL 如何处理死锁?杀死进程MySQL表间隙锁排他锁共享锁分析行锁定行锁优化 四、如何避免发生…

论文阅读|SMPL2015

摘要: 我们提出了一个学习过的人体形状和姿势相关的形状变化模型,该模型比以前更准确建模并与现有的图形管线兼容。我们的蒙皮多人线性模型(SMPL)是基于蒙皮顶点的模型,可以准确地表示各种各样的人体姿态。模型的参数是…

densepose与SMPL之IUV坐标转XYZ坐标

具体流程 一、SMPL模型 SMPL模型拥有6890个XYZ坐标的3D人体点,目前第一步需要将这6890个人体点进行分析,并将不同部位的点位进行归并,具体分为以下几个部分:头部,胸部,腰部,左臂,右…

人体捕捉:《SMPL》

《SMPL: A Skinned Multi-Person Linear Model》 作者:Matthew Loper 主页:https://smpl.is.tue.mpg.de/ 时间:2015 文章目录 Table of NotationModel generation functionsModel input parameters(controls)Model parameters(parameters le…

python3跑通smpl模型_Python smpl-pytorch包_程序模块 - PyPI - Python中文网

Pythorch的SMPL层 SMPL人体[1]层为PyTorch(用v0.4和v1.x测试) 是一个可微的pytorch层,它确定地从姿势和形状参数映射到人体关节和顶点。 它可以作为可微层集成到任何体系结构中,以预测实体网格。 代码由Yana Hasson改编自manopth存储库。 安装 您可以从PyPI:安装smpl pytorc…

SMPL STAR人体模型系列

SCAPE: 人体模型 SMPL: 人体模型 SMPL-H: 人体手 SMPL-X: 人体手人脸(FLAME) STAR: SMPL的改进版 人体模型主要思想是将pose, shape解耦,用参数化的方式描述人体表面的信息。 SCAPE:基于三角面片deformable的人体模型。 SMPL…

SMPL模型及源码解读

Contents Preface一、模型解读二、源码解读Citation Preface SMPL主要是人体三维重建常用模型,本文主要对模型及源码进行了解读(自己的理解不一定正确),为以后更好的利用此模型进行人体重建打好基础! 一、模型解读 二…

SMPL:数据增强之处理pose和3d点

SMPL 是一个低维度的参数化人体模型,SMPL系数有pose和shape,在训练基于SMPL的3Dmesh重建任务时候,一般需要进行数据增强 例如旋转,镜像等,那当GT 中有pose和3d点的时候,怎么处理? 一 &#xff1…

SMPL源码实现及相关问题

SMPL源码实现及相关问题 SMPL模型代码结构SMPL实现具体步骤一、环境的配置二、相关库的安装三、运行SMPL CITATION SMPL模型 SMPL模型官网http://smpl.is.tue.mpg.de/,里面可以下载模型和观看演示视频,只不过要先注册一个账号。SMPL由Michael J. Black团…

smpl-x论文学习-部分翻译

论文地址:Expressive Body Capture: 3D Hands, Face, and Body from a Single Image 知乎大佬的讲解:https://zhuanlan.zhihu.com/p/137235901 另一位大佬的讲解:https://posts.careerengine.us/p/5f23a5898988c12b4302afb6 1. 定性结果 和 …

人体动作捕捉与SMPL模型 (mocap and SMPL model)

人体动作捕捉与SMPL模型 (mocap and SMPL model) FesianXu 2020.7.5 前言 笔者最近在做和motion capture动作捕捉相关的项目,学习了一些关于人体3D mesh模型的知识,其中以SMPL模型最为常见,笔者特在此进行笔记,希望对大家有帮助&a…

blender 绘制离散顶点, SMPL骨架绘制

给定一些点,如何绘制出来,借助 blender 看下效果。纠结于 unity 还是 blender, 最终还是 blender 了。 目前还都不太满意,思路一比较靠谱,但是需要更复杂的计算 思路一,第二版,已完成&#xff…

SMPL-CN

paper-reading 为方便理解smpl文章的主要实现思想,此文为论文中文解读,资料来源zju。 日后有空,会写出论文的主要推导过程以及值得注意的重点。 摘要: 我们提出了一个人体形状和姿势相关的形状变化的学习模型,它比以前…

SMPL源码解读

这是源码的整体结构,先简单说一下各个文件里面是什么。 一、models文件 包含3个模型的.pkl文件,.pkl文件是python提供的可以以字符串的形式记录对象、变量的文件格式。这个模型里面包括了: 1.J_regressor_prior:关节回归矩阵的先验,保存形…

SMPL 人体模型简要

smpl是指2015 马普的一篇文章“SMPL: a skinned multi-person linear model”中构建的人体参数化三维模型,人体可以理解为是一个基础模型和在该模型基础上进行形变的总和,在形变基础上进行PCA,得到刻画形状的低维参数——形状参数&#xff08…

SMPL-论文解读

文章目录 创新点算法shape blend shape:pose blend shapeJoint locationSMPL model 训练过程Pose Parameter Trainingjoint regressorShape Parameter TrainingOptimization summary DMPL实验结论 论文: 《SMPL: A skinned multi-person linear model》…

SMPL模型进阶

SMPL模型是一种参数化人体模型,是马普所提出的一种人体建模方法,该方法可以进行任意的人体建模和动画驱动。这种方法与传统的LBS的最大的不同在于其提出的人体姿态影像体表形貌的方法,这种方法可以模拟人的肌肉在肢体运动过程中的凸起和凹陷。…

SMPL源代码实现和模型解读

对于SLAM的工作已经告一段落了,传统的人体动态三维重建也要告一段落了,由于课题研究的方向是基于图像\视频的人体三维重建,三维shape、pose的恢复:所以今天和大家交流的是SMPL模型,对于SMPL模型的理论部分欢迎大家交流…

SMPL简析

SMPL模型是一种参数化人体模型,是马普所提出的一种人体建模方法。这种方法可以模拟人的肌肉在肢体运动过程中的凸起和凹陷。因此可以避免人体在运动过程中的表面失真,可以精准的刻画人的肌肉拉伸以及收缩运动的形貌。 该模型可以通过身体形状参数和姿势参…

SMPL学习笔记

文章目录 前言一、SMPL概述1.形状参数( β \beta β)2.姿态参数( θ \theta θ) 二、体姿转换过程原理1.基于形状的混合成形(Shape Blend Shapes)2.基于姿态的混合成形 (Pose Blend Shapes)3.蒙皮 (Skinning) 三、具体过程分析1.基于形状的混合成形2.基于姿态的混合成形3.骨骼点…