mysql死锁介绍以及解决

这篇具有很好参考价值的文章主要介绍了mysql死锁介绍以及解决。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

什么是死锁

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

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

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

Mysql对死锁的检测与处理

mysql死锁定义

内容来自MySQL技术内幕InnoDB存储引擎
mysql死锁介绍以及解决

解决方案

一是超时机制 即两个事务相互等待时,一旦等待时间超过一个阈值,那么超时事务回滚释放资源,另一个事务就能正常执行了。
在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的后面mysql死锁介绍以及解决

在 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 的边.
根据事务间的等待关系构建图
mysql死锁介绍以及解决

可以发现存在回路(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 死锁。
mysql死锁介绍以及解决
在上述操作中,会话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 资源。

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

死锁产生的案例

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

  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、循环等待
那么解决总体的思路就是文章来源地址https://www.toymoban.com/news/detail-405491.html

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

到了这里,关于mysql死锁介绍以及解决的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • MySQL什么情况下会死锁,发生了死锁怎么处理呢?

    🏆作者简介,黑夜开发者,CSDN领军人物,全栈领域优质创作者✌,CSDN博客专家,阿里云社区专家博主,2023年6月CSDN上海赛道top4。 🏆数年电商行业从业经验,历任核心研发工程师,项目技术负责人。 🏆本文已收录于PHP专栏:MySQL的100个知识点。 🎉欢迎 👍点赞✍评论⭐收

    2024年02月08日
    浏览(38)
  • 什么是死锁?互斥锁进入死锁怎么解决?

    (1)死锁是一组相互竞争资源的线程因为他们之间得到互相等待导致“永久“阻塞的现象; (你等我 我等你 你不放我也不放 就导致“永久“阻塞的现象) (2)死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外

    2024年02月06日
    浏览(53)
  • 【Redis】-使用Lua脚本解决多线程下的超卖问题以及为什么?

    一.多线程下引起的超卖问题呈现 1.1.我先初始化库存数量为1、订单数量为0 1.2.然后我开启3个线程去执行业务 业务为:判断如果说库存数量大于0,则库存减1,订单数量加1 结果为:库存为-2,订单数量为3 原因:如下图所示,这是因为分别有6个指令(3个库存减1指令,3个订单

    2024年02月03日
    浏览(54)
  • Mysql 死锁和死锁的解决方案

    前置知识:需要了解锁的种类,如表锁、行锁;行锁又分为记录锁、间隙锁、临键锁等等;什么情况下会加表锁,什么情况下会加行锁,什么情况下会加临键锁,什么情况锁会升级等等。。。网上讲解很多,这里就不单独讲述了 一 表死锁 产生原因: 用户A访问表A(锁住了表

    2024年02月02日
    浏览(61)
  • 什么是死锁,如何解决

    死锁是指在执行过程中,两个或两个以上的进程(或线程)由于竞争资源或彼此通信而阻塞,导致无法继续执行的情况。 如果没有外部干预,这些进程将无法向前推进。 这种状态被称为系统死锁或死锁产生。 这些相互等待的进程被称为死锁进程。 死锁案例: 🕵️‍♀️面

    2024年02月15日
    浏览(44)
  • 记录生产mysql死锁解决过程

    最近生产上每个星期都会有几次死锁告警异常,今天终于给处理了,待后续观察,记录下整个过程。 环境:springboot、mybatis、mysql(RC隔离级别) 表结构: 阿里云日志告警: 立刻查找生产mysql死锁日志: 根据事物1和事物2的sql,以及java报错的堆栈信息,找到发生问题的代码。

    2024年02月11日
    浏览(51)
  • MySQL遇到过死锁问题吗,你是如何解决的?

    MySQL遇到过死锁问题吗,你是如何解决的? 问题解析 死锁,就是两个或者两个以上的线程在执行过程中,去争夺同一个共享资源导致互相等待的现象。 在没有外部干预的情况下,线程会一直处于阻塞状态,无法往下执行。 要想真正产生死锁,必须同时满足四个条件。互斥条

    2024年02月02日
    浏览(52)
  • MySQL的index merge(索引合并)导致数据库死锁分析与解决方案

    在DBS-集群列表-更多-连接查询-死锁中,看到9月22日有数据库死锁日志,后排查发现是因为mysql的优化-index merge(索引合并)导致数据库死锁。 index merge(索引合并):该数据库查询优化的一种技术,在mysql 5.1之后进行引入,它可以在多个索引上进行查询,并将结果合并返回。 在

    2024年02月08日
    浏览(82)
  • java死锁、线程状态、线程通信、线程池

    java实现多线程: [1]继承Thread类并重写run方法 [2]实现Runnable接口 线程Thread中常用的方法: setName(): Thread.currentThread().getName(): ​ static void sleep(); static void yield(): join(): setDeamon()设置后台线程 线程安全问题: ---当多个线程共享同一个资源时,对该资源的操作就会出现线程安全问题。

    2024年02月13日
    浏览(39)
  • 【Linux】线程互斥 -- 互斥锁 | 死锁 | 线程安全

    我们写一个多线程同时访问一个全局变量的情况(抢票系统),看看会出什么bug: 假如创建4个线程同时抢票,总票数有10000张,每个线程抢到票以后减一,按照正常情况我们应该是抢票到0截至。 多个线程交叉执行本质:就是让调度器尽可能的频繁发生线程调度与切换 线程一般

    2024年02月14日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包