草庐IT

mysql死锁介绍以及解决

氵奄不死的鱼 2023-11-20 原文

什么是死锁

死锁是2+个线程在执行过程中, 因争夺资源而造成的相互等待的现象,若无外力作用,它们将无法推进下去。
死锁产生的4个必要条件
互斥条件
指进程对所分配的资源进行排他性使用,即一段时间内某资源只有一个进程占用,其他的进程请求资源只能等待,直至被占有资源的进程得到释放。
请求和保留条件
指进程至少保持占用一个资源,但又提出新的资源请求,而该资源正被其他进程占用,此时请求进程阻塞,但对以获得的其他资源保持不放。
不剥夺条件
指进程已获得的资源,在未使用完之前,不能剥夺,只能使用完时由自己释放。
环路等待条件
值发生死锁时,必然存在一个进程占用资源的环形链,即进程集合(P0,P1,P2, … Pn),P0等待P1资源释放,P1等待P2资源释放,P3等待 … Pn等待P0资源释放。
对应到mysql中存在的互斥锁,和事务对资源使用排他锁占用,并且事务不结束不会释放,事务之间可会出现资源之间的相互占用,相互等待,因此看来,mysql中是会出现死锁的。

死锁导致长时间阻塞的危害

众所周知,数据库的连接资源是很珍贵的,如果一个连接因为事务阻塞长时间不释放,那么后面新的请求要执行的sql也会排队等待,越积越多,最终会拖垮整个应用。一旦你的应用部署在微服务体系中而又没有做熔断处理,由于整个链路被阻断,那么就会引发雪崩效应,导致很严重的生产事故。

Mysql对死锁的检测与处理

mysql死锁定义

内容来自MySQL技术内幕InnoDB存储引擎

解决方案

一是超时机制 即两个事务相互等待时,一旦等待时间超过一个阈值,那么超时事务回滚释放资源,另一个事务就能正常执行了。
在InnoDB存储引擎中,,参数innodb lock_wait timeout 用来设置事务超时的时间

超时机制虽然简单,但是其仅通过超时后对事务进行回滚的方式来处理,或者说其是根据 FIFO 的顺序选择回滚对象。但若超时的事务所占权重比较大,如事务操作更新了很多行,占用了较多的 undo log,这时采用 FIFO 的方式,就显得不合适了,因为回滚这个事务的时间相对另一个事务所占用的时间可能会很多。另一方面,事务时间的等待时间过长,造成的阻塞时间过长,很多情况下也无法接受

因此,除了超时机制,当前数据库还都普遍采用wait-for graph(等待图)的方式来进行死锁检测。较之超时的解决方案,这是一种更为主动的死锁检测方式。InnoDB 存储引擎也采用的这种方式。wait-for graph 要求数据库保存以下两种信息:
锁的信息链表
事务等待链表
通过上述链表可以构造出一张图,而在这个图中若存在回路,就代表存在死锁,因此资源间相互发生等待。在 wait-for graph 中,事务为图中的节点。而在图中,事务 T1指向T2边的定义为:
事务T1等待事务T2所占用的资源
事务 T1 最终等待 T2 所占用的资源,也就是事务之间在等待相同的资源,而事务
T1发生在事务 T2的后面

在 Transaction Wait Lists 中可以看到共有4个事务t1、t2、t3、t4,故在 wait-forgraph 中应有4个节点。而事务 t2 对rowl 占用x锁,事务t1对row2占用s锁。事务tl需要等待事务t2中row1的资源,因此在 wait-for graph 中有条边从节点t1 指向节点t2。事务t2需要等待事务 t1、t4 所占用的 row2 对象,故而存在节点t2 到节点 t1t4 的边。同样,存在节点t3 到节点 t1、t2、t4 的边.
根据事务间的等待关系构建图

可以发现存在回路(t1,t2,因此存在死锁。通过上述的介绍,可以发现wait-for graph 是一种较为主动的死锁检测机制,在每个事务请求锁并发生等待时都会判断是否存在回路,若存在则有死锁,通常来说InnoDB存储引擎选择回滚undo量最小的事务
wait-for graph的死锁检测通常采用深度优先的算法实现在InnoDB1.2版本之前,都是采用递归方式实现。而从1.2 版本开始,对 wait-for graph 的死锁检测进行了优化,将递归用非递归的方式实现,从而进一步提高了 InnDB存储引擎的性能

死锁示例

如果程序是串行的,那么不可能发生死锁。死锁只存在于并发的情况,而数据库本身就是一个并发运行的程序,因此可能会发生死锁。表 6-18 的操作演示了死锁的一种经典的情况,即A等待 B,B在等待 A,这种死锁问题被称为AB-BA 死锁。

在上述操作中,会话B 中的事务抛出了 1213 这个错误提示,即表示事务发生了死锁。死锁的原因是会话A 和B 的资源在相等待。大多数的死锁InoDB 存储引擎本身可以侦测到,不需要人为进行干预。但是在上面的例子中,在会话 B 中的事务抛出死锁异常后,会话 A 中马上得到了记录为2的这个资源,这其实是因为会话 B 中的事务发生了回滚,否则会话A 中的事务是不可能得到该资源的。InnoDB存储引擎并不会回滚大部分的错误异常,但是死锁除外。发现死锁后,InnoDB存储引擎会马上回滚一个事务,这点是需要注意的。因此如果在应用程序中捕获了 1213这个错误,其实并不需要对其进行回滚。

疑问

那么看到这里看起来 mysql对于死锁的处理是挑选一个回滚代价小的事务进行归滚,接触循环等待的条件,对死锁进行解除。
但是那么为什么在业务上还是碰到锁等待的问题导致阻塞的问题?

业务上死锁案例与解决办法

死锁检测被关闭

mysql死锁检测被死锁检测机制被关闭

show VARIABLES like  'innodb_deadlock_detect' -- 查看当前死锁检测是否开启

set global innodb_deadlock_detect = OFF; --设置死锁检测关闭

set global innodb_deadlock_detect = ON; --设置死锁检测开启

可以设置innodb_deadlock_detect=on 来开启死锁检测。死锁检测在发生死锁的时候,能够快速发现并进行处理,回滚并重新启动。但是死锁检测会比较好资源。当每个新来的被堵住的线程,都要判断会不会由于自己的加入导致了死锁,这是一个时间复杂度是 O(n) 的操作。假设有 1000 个并发线程要同时更新同一行,那么死锁检测操作可能就是 100 万的量级。虽然最终检测的结果可能没有死锁,但是这期间要消耗大量的 CPU 资源。

死锁检测时默认开启的,如果被关闭,那么会导致碰到事务之间相互等待的死锁问题,就只能等待事务超时后回滚释放资源

死锁产生的案例

案例一:事物之间对资源访问顺序的交替

  1. 出现原因:A用户问A资源锁住A时请求B资源,B用户问B资源锁住B时请求A资源,产生死锁。
    就是经典的为AB-BA 死锁
    解决方法:多用户操作多表资源时,按照相同资源访问顺序进行处理。
    这种死锁比较常见,是由于程序的BUG产生的,除了调整的程序的逻辑没有其它的办法。仔细分析程序的逻辑,对于数据库的多表操作时**,尽量按照相同的顺序进行处理,尽量避免同时锁定两个资源,如操作A和B两张表时,总是按先A后B的顺序处理**,** 必须同时锁定两个资源时,要保证在任何时刻都应该按照相同的顺序来锁定资源。**

案例二:并发修改同一记录

  1. 出现原因:两个事务都对同一条记录做更改,都是先读取后修改。读取时加S锁,写入时加X锁
    例如,事务1和事务2同时执行下段代码,,两个事务都完成了加S锁之后再尝试加S锁,那么都无法获取,阻塞无法提交释放资源,导致死锁

set autocommit=0;
START TRANSACTION;
# 获取A 的余额并存入A_balance变量:80
SELECT * from account where user_id = 'A' lock in share MODE;


update account set balance = 0 where user_id = 'A';

解决方法:
a. 使用乐观锁进行控制。
乐观锁大多是基于数据版本(Version)记录机制实现。即为数据增加一个版本标识,在基于数据库表的版本解决方案中,一般是通过为数据库表增加一个“version”字段来实现。读取出数据时,将此版本号一同读出,之后更新时,对此版本号加一。此时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号大于数据库表当前版本号,则予以更新,否则认为是过期数据。乐观锁机制避免了长事务中的数据库加锁开销(用户A和用户B操作过程中,都没有对数据库数据加锁),大大提升了大并发量下的系统整体性能表现。需要注意的是,由于乐观锁机制是在我们的系统中实现,来自外部系统的用户更新操作不受我们系统的控制,因此可能会造成脏数据被更新到数据库中。
使用乐观锁情况下,查询数据时不需要上S锁,例如在RC和RR隔离级别下,会通过readview,mvcc机制读取数据,读取数据不会上锁,,最终更新数据时会检测版本号,如果版本号已经不等于当前查询出的数据,那么更新失败,避免脏写又避免了死锁。

b. 使用悲观锁进行控制。
悲观锁大多数情况下依靠数据库的锁机制实现,
比如第一次读取就加X锁,保证独占

SELECT * from account where user_id = 'A' for update;

,以保证操作最大程度的独占性。但随之而来的就是数据库性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。如一个金融系统,当某个操作员读取用户的数据,并在读出的用户数据的基础上进行修改时(如更改用户账户余额),如果采用悲观锁机制,也就意味着整个操作过程中(从操作员读出数据、开始修改直至提交修改结果的全过程,甚至还包括操作员中途去煮咖啡的时间),数据库记录始终处于加锁状态,可以想见,如果面对成百上千个并发,这样的情况将导致灾难性的后果。所以,采用悲观锁进行控制时一定要考虑清楚。

案例三:索引不当导致全表扫描

我们知道,mysql锁算法最终锁的是索引,如果更新语句条件围台过大,无法确定主键/索引范围,那么把行级锁/间隙锁上升为表级锁,锁的粒度庞大,扫描时间更长,占用资源多,且耗时,会导致死锁的概率大大增加!
如果在事务中执行了一条不满足条件的语句,执行全表扫描,把行级锁上升为表级锁,多个这样的事务执行后,就很容易产生死锁和阻塞。类似的情况还有当表中的数据量非常庞大而索引建的过少或不合适的时候,使得经常发生全表扫描,最终应用系统会越来越慢,最终发生阻塞或死锁。

解决方法:

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

案例四 存在耗时事务

耗时事务的存在导致,其一直不提交,占用的资源一直得不到释放,其他事务只有阻塞等待资源被释放。注意耗时事务的存在导致资源长时间得不到释放,会增加死锁的概率
解决办法
保持事务简短并在一个批处理中
在同一数据库中并发执行多个需要长时间运行的事务时通常发生死锁。事务运行时间越长,其持有排它锁或更新锁的时间也就越长,从而堵塞了其它活动并可能导致死锁。保持事务在一个批处理中,可以最小化事务的网络通信往返量,减少完成事务可能的延迟并释放锁。

案例五 网络问题导致的死连接的产生(出现概率很小)

所谓死连接,指的是客户端发起开启事务的请求,执行更新操作之后在未提交事务之前,因为网络等原因(类似拔网线这样的操作重启,关闭线程这样,不会导致死连接)。不能再与mysql服务器通信,那么这个事务就一直得不到提交。直到对于mysql服务器来说这条连接心跳超时或者事务超时,事务才能得到回滚资源才能得到释放。
这种情况应该及时kill掉相关事务。

总结

首先在没有特殊要求情况下死锁检测最好不要关闭,死锁检测会对死锁处理,资源释放,避免阻塞,即使产生死锁,对系统的影响也很小。
死锁的预防就是从死锁的四个条件上入手
死锁发生的条件:
1、资源不能共享,需要只能由一个进程或者线程使用
2、请求且保持,已经锁定的资源自给保持着不释放
3、不剥夺,自给申请到的资源不能被别人剥夺
4、循环等待
那么解决总体的思路就是

  1. 使用乐观锁mvcc机制,读取数据不上锁,在读情况下共享资源
  2. 保证资源的加锁顺序,避免循环等待的产生
  3. 减少对资源的占用时间和占用范围,避免长事务,锁粒度变大的情况,可以大大减少死锁产生的概率

有关mysql死锁介绍以及解决的更多相关文章

  1. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  2. 屏幕录制为什么没声音?检查这2项,轻松解决 - 2

    相信很多人在录制视频的时候都会遇到各种各样的问题,比如录制的视频没有声音。屏幕录制为什么没声音?今天小编就和大家分享一下如何录制音画同步视频的具体操作方法。如果你有录制的视频没有声音,你可以试试这个方法。 一、检查是否打开电脑系统声音相信很多小伙伴在录制视频后会发现录制的视频没有声音,屏幕录制为什么没声音?如果当时没有打开音频录制,则录制好的视频是没有声音的。因此,建议在录制前进行检查。屏幕上没有声音,很可能是因为你的电脑系统的声音被禁止了。您只需打开电脑系统的声音,即可录制音频和图画同步视频。操作方法:步骤1:点击电脑屏幕右下侧的“小喇叭”图案,在上方的选项中,选择“声音”。 步骤2:在“声

  3. 【高数】用拉格朗日中值定理解决极限问题 - 2

    首先回顾一下拉格朗日定理的内容:函数f(x)是在闭区间[a,b]上连续、开区间(a,b)上可导的函数,那么至少存在一个,使得:通过这个表达式我们可以知道,f(x)是函数的主体,a和b可以看作是主体函数f(x)中所取的两个值。那么可以有,  也就意味着我们可以用来替换 这种替换可以用在求某些多项式差的极限中。方法: 外层函数f(x)是一致的,并且h(x)和g(x)是等价无穷小。此时,利用拉格朗日定理,将原式替换为 ,再进行求解,往往会省去复合函数求极限的很多麻烦。使用要注意:1.要先找到主体函数f(x),即外层函数必须相同。2.f(x)找到后,复合部分是等价无穷小。3.要满足作差的形式。如果是加

  4. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  5. Unity 热更新技术 | (三) Lua语言基本介绍及下载安装 - 2

    ?博客主页:https://xiaoy.blog.csdn.net?本文由呆呆敲代码的小Y原创,首发于CSDN??学习专栏推荐:Unity系统学习专栏?游戏制作专栏推荐:游戏制作?Unity实战100例专栏推荐:Unity实战100例教程?欢迎点赞?收藏⭐留言?如有错误敬请指正!?未来很长,值得我们全力奔赴更美好的生活✨------------------❤️分割线❤️-------------------------

  6. 使用canal同步MySQL数据到ES - 2

    文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co

  7. 深度学习部署:Windows安装pycocotools报错解决方法 - 2

    深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal

  8. 阿里云国际版免费试用:如何注册以及注意事项 - 2

    作为新的阿里云用户,您可以50免费试用多种优惠,价值高达1,700美元(或8,500美元)。这将让您了解和体验阿里云平台上提供的一系列产品和服务。如果您以个人身份注册免费试用,您将获得价值1,700美元的优惠。但是,如果您是注册公司,您可以选择企业免费试用,提交基本信息通过企业实名注册验证,即可开始价值$8,500的免费试用!本教程介绍了如何设置您的帐户并使用您的免费试用版。​关于免费试用在我们开始此试用之前,您还必须遵守以下条款和条件才能访问您的免费试用:只有在一年内创建的账户才有资格获得阿里云免费试用。通过此免费试用优惠,用户可以免费试用免费试用活动页面上列出的每种产品一次。如果您有多个帐

  9. ruby-on-rails - 无法安装 mysql2 0.3.14 gem - 2

    我看到其他人也遇到过类似的问题,但没有一个解决方案对我有用。0.3.14gem与其他gem文件一起存在。我已经完全按照此处指示完成了所有操作:https://github.com/brianmario/mysql2.我仍然得到以下信息。我不知道为什么安装程序指示它找不到include目录,因为我已经检查过它存在。thread.h文件存在,但不在ruby​​目录中。相反,它在这里:C:\RailsInstaller\DevKit\lib\perl5\5.8\msys\CORE\我正在运行Windows7并尝试在Aptana3中构建我的Rails项目。我的Ruby是1.9.3。$gemin

  10. ruby - 如何更快地解决 project euler #21? - 2

    原始问题Letd(n)bedefinedasthesumofproperdivisorsofn(numberslessthannwhichdivideevenlyinton).Ifd(a)=bandd(b)=a,whereab,thenaandbareanamicablepairandeachofaandbarecalledamicablenumbers.Forexample,theproperdivisorsof220are1,2,4,5,10,11,20,22,44,55and110;therefored(220)=284.Theproperdivisorsof284are1,2,

随机推荐