一、 什么是死锁

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

二、 死锁产生的四个必要条件

 1.互斥条件:指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放2.请求和保持条件:指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放3.不剥夺条件:指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放    4.环路等待条件:指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。

下列方法有助于最大限度地降低死锁:
虽然不能完全避免死锁,但可以使死锁的数量减至最少。将死锁减至最少可以增加事务的吞吐量并减少系统开销,因为只有很少的事务回滚,而回滚会取消事务执行的所有工作。由于死锁时回滚而由应用程序重新提交。
(1)按同一顺序访问对象。
如果所有并发事务按同一顺序访问对象,则发生死锁的可能性会降低。例如,如果两个并发事务获得 Supplier 表上的锁,然后获得 Part 表上的锁,则在其中一个事务完成之前,另一个事务被阻塞在 Supplier 表上。第一个事务提交或回滚后,第二个事务继续进行。不发生死锁。将存储过程用于所有的数据修改可以标准化访问对象的顺序。

(2)避免事务中的用户交互。
避免编写包含用户交互的事务,因为运行没有用户交互的批处理的速度要远远快于用户手动响应查询的速度,例如答复应用程序请求参数的提示。例如,如果事务正在等待用户输入,而用户去吃午餐了或者甚至回家过周末了,则用户将此事务挂起使之不能完成。这样将降低系统的吞吐量,因为事务持有的任何锁只有在事务提交或回滚时才会释放。即使不出现死锁的情况,访问同一资源的其它事务也会被阻塞,等待该事务完成。

(3)保持事务简短并在一个批处理中。
在同一数据库中并发执行多个需要长时间运行的事务时通常发生死锁。事务运行时间越长,其持有排它锁或更新锁的时间也就越长,从而堵塞了其它活动并可能导致死锁。
保持事务在一个批处理中,可以最小化事务的网络通信往返量,减少完成事务可能的延迟并释放锁。

(4)使用低隔离级别。
确定事务是否能在更低的隔离级别上运行。执行提交读允许事务读取另一个事务已读取(未修改)的数据,而不必等待第一个事务完成。使用较低的隔离级别(例如提交读)而不使用较高的隔离级别(例如可串行读)可以缩短持有共享锁的时间,从而降低了锁定争夺。

(5)使用绑定连接。
使用绑定连接使同一应用程序所打开的两个或多个连接可以相互合作。次级连接所获得的任何锁可以象由主连接获得的锁那样持有,反之亦然,因此不会相互阻塞。

三、 如何处理死锁

1) 锁模式

1.共享锁(S)
由读操作创建的锁,防止在读取数据的过程中,其它事务对数据进行更新;其它事务可以并发读取数据。共享锁可以加在表、页、索引键或者数据行上。在SQL SERVER默认隔离级别下数据读取完毕后就会释放共享锁,但可以通过锁提示或设置更高的事务隔离级别改变共享锁的释放时间。
2.独占锁(X)
对资源独占的锁,一个进程独占地锁定了请求的数据源,那么别的进程无法在此数据源上获得任何类型的锁。独占锁一致持有到事务结束。
3.更新锁(U)
更新锁实际上并不是一种独立的锁,而是共享锁与独占锁的混合。当SQL SERVER执行数据修改操作却首先需要搜索表以找到需要修改的资源时,会获得更新锁。
更新锁与共享锁兼容,但只有一个进程可以获取当前数据源上的更新锁,
其它进程无法获取该资源的更新锁或独占锁,更新锁的作用就好像一个序列化阀门(serialization gate),将后续申请独占锁的请求压入队列中。持有更新锁的进程能够将其转换成该资源上的独占锁。更新锁不足以用于更新数据—实际的数据修改仍需要用到独占锁。对于独占锁的序列化访问可以避免转换死锁的发生,更新锁会保留到事务结束或者当它们转换成独占锁时为止。
4. 意向锁(IX,IU,IS)
意向锁并不是独立的锁定模式,而是一种指出哪些资源已经被锁定的机制。
如果一个表页上存在独占锁,那么另一个进程就无法获得该表上的共享表锁,这种层次关系是用意向锁来实现的。进程要获得独占页锁、更新页锁或意向独占页锁,首先必须获得该表上的意向独占锁。同理,进程要获得共享行锁,必须首先获得该表的意向共享锁,以防止别的进程获得独占表锁。
5. 特殊锁模式(Sch_s,Sch_m,BU)
SQL SERVER提供3种额外的锁模式:架构稳定锁、架构修改锁、大容量更新锁。
6.转换锁(SIX,SIU,UIX)
转换锁不会由SQL SERVER 直接请求,而是从一种模式转换到另一种模式所造成的。SQL SERVER 2008支持3种类型的转换锁:SIX、SIU、UIX.其中最常见的是SIX锁,如果事务持有一个资源上的共享锁(S),然后又需要一个IX锁,此时就会出现SIX。
7.键范围锁
键范围锁是在可序列化隔离级别中锁定一定范围内数据的锁。保证在查询数据的键范围内不允许插入数据。
SQL SERVER 锁模式

缩写
锁模式
说明

S
Shared
允许其他进程读取但不能修改锁定的资源

X
Exclusive
防止别的进程读取或者修改锁定资源中的数据

U
Update
防止其它进程获取更新锁或独占锁;在搜索要修改的数据时使用

IS
Intent shared
表示该资源的一个组件被共享锁锁定了。只有在表或页级别才能获得这类锁

IU
Intent update
表示该资源的一个组件被更新锁锁定了。只有在表或页级别才能获得这类锁

IX
Intent exclusive
表示该资源的一个组件被独占锁锁定了。只有在表或页级别才能获得这类锁

SIX
Shared with intent exclusive
表示一个正持有共享锁的资源还有一个组件(一页或一行)被独占锁锁定了

SIU
Shared with intent Update
表示一个正持有共享锁的资源还有一个组件(一页或一行)被更新锁锁定了

UIX
Update with intent exclusive
表示一个正持有更新锁的资源还有一个组件(一页或一行)被独占锁锁定了

Sch-S
Schema stability
表示一个使用该表的查询正在被编译

Sch-M
Schema modification
表示表的结构正在被修改

BU
Bulk Update
在一个大容量复制操作将数据导入表中并且(手动或自动)应用了TABLOCK查
询提示时使用

2) 锁粒度

SQL SERVER 可以在表、页、行等级别锁定用户的数据资源即非系统资源(系统资源是用闩锁来保护的)。此外SQL SERVER 还可以锁定索引键和索引键范围。
通过sys.dm_tran_locks视图可以查看谁被锁定了(如行,键,页)、锁的模式以及特定资源的标志符。基于sys.dm_tran_locks视图创建如下视图用于查看锁定的资源以及锁模式(通过这个视图可以查看事务锁定的表、页、行以及加在数据资源上的锁类型)。

3) 如何跟踪死锁

通过选择sql server profiler 事件中的如下选项就可以跟踪到死锁产生的相关语句。

5) 如何解决死锁

一般通过执行计划可以看出,在查找需要更新的数据时使用的是索引扫描,比较耗费性能,这样就造成锁定资源时间过长,增加了语句并发执行时产生死锁的概率。
处理方式:
1. 在表上建立一个聚集索引。
2. 对语句更新的相关字段建立包含索引。
优化后该语句执行计划如下:
优化后的执行计划使用了索引查找,将大幅提升该查询语句的性能,降低了锁定资源的时间,同时也减少了锁定资源的范围,这样就降低了锁资源循环等待事件发生的概率,对于预防死锁的发生会有一定的作用。
死锁是无法完全避免的,但如果应用程序适当处理死锁,对涉及的任何用户及系统其余部分的影响可降至最低(适当处理是指发生错误1205时,应用程序重新提交批处理,第二次尝试大多能成功。一个进程被杀死,它的事务被取消,它的锁被释放,死锁中涉及到的另一个进程就可以完成它的工作并释放锁,所以就不具备产生另一个死锁的条件了。)

四、 如何预防死锁

阻止死锁的途径就是避免满足死锁条件的情况发生,为此我们在开发的过程中需要遵循如下原则:

1.尽量避免并发的执行涉及到修改数据的语句。
2.要求每一个事务一次就将所有要使用到的数据全部加锁,否则就不允许执行。
3.预先规定一个加锁顺序,所有的事务都必须按照这个顺序对数据执行封锁。如不同的过程在事务内部对对象的更新执行顺序应尽量保证一致。
4.每个事务的执行时间不可太长,对程序段的事务可考虑将其分割为几个事务。在事务中不要求输入,应该在事务之前得到输入,然后快速执行事务。
5.使用尽可能低的隔离级别。

6.数据存储空间离散法。该方法是指采用各种手段,将逻辑上在一个表中的数据分散的若干离散的空间上去,以便改善对表的访问性能。主要通过将大表按行或者列分解为若干小表,或者按照不同的用户群两种方法实现。
7.编写应用程序,让进程持有锁的时间尽可能短,这样其它进程就不必花太长的时间等待锁被释放。

死锁的概念:

如果一组进程中的每一个进程都在等待仅由该组进程中的其他进程才能引发的事件,那么改组进程是死锁的。

死锁的常见表现:

死锁不仅会发生多个进程中,也会发生在一个进程中。
(1)多进程死锁:有进程A,进程B,进程A拥有资源1,需要请求正在被进程B占有的资源2。而进程B拥有资源2,请求正在被进程A战友的资源1。两个进程都在等待对方释放资源后请求该资源,而相互僵持,陷入死锁。
(2)单进程死锁:进程A拥有资源1,而它又在请求资源1,而它所请求的资源1必须等待该资源使用完毕得到释放后才可被请求。这样,就陷入了自己的死锁。

产生死锁的原因:

(1)进程推进顺序不当造成死锁。
(2)竞争不可抢占性资源引起死锁。
(3)竞争可消耗性资源引起死锁。

死锁的四个必要条件(四个条件四者不可缺一):

(1)互斥条件。某段时间内,一个资源一次只能被一个进程访问。
(2)请求和保持条件。进程A已经拥有至少一个资源,此时又去申请其他资源,而该资源又正在被进程使用,此时请求进程阻塞,但对自己已经获得的资源保持不放。
(3)不可抢占资源。进程已获得的资源在未使用完不能被抢占,只能在自己使用完时由自己释放。
(4)循环等待序列。存在一个循环等待序列P0P1P2……Pn,P0请求正在被进程P1占有的资源,P1请求正在被P2占有的资源……Pn正在请求被进程P0占有的资源。

解除死锁的两种方法:

(1)终止(或撤销)进程。终止(或撤销)系统中的一个或多个死锁进程,直至打破循环环路,使系统从死锁状态中解除出来。
(2)抢占资源。从一个或多个进程中抢占足够数量的资源,分配给死锁进程,以打破死锁状态。

五,死锁场景

本文死锁场景皆为工作中遇到(或同事遇到)并解决的死锁场景,写这篇文章的目的是整理和分享,欢迎指正和补充,本文死锁场景包括:

行锁导致死锁
gap lock/next keys lock导致死锁
index merge 导致死锁
唯一索引冲突导致死锁

注:以下场景隔离级别均为默认的Repeatable Read;

1.行锁导致死锁

死锁原因详解:

1.两个事务执行过程时间上有交集,并且过程发生在两者提交之前
2.事务1更新uid=1的记录,事务2更新uid=2的记录,在RR级别,由于uid是唯一索引,因此两个事务将分别持有uid=1和2所在行的独占锁
3.事务1执行到第二条更新语句时,发现uid=2的行被锁住,进入阻塞等待锁释放;
4.事务2执行到第二条语句时发现uid=1的行被锁,同样进入阻塞
5.两个事务互相等待,死锁产生。

相应业务案例和解决方案:
该场景常见于事务中存在for循环更新某条记录的情况,死锁日志显示lock_mode X locks rec but not gap waiting(即行锁而非间隙锁),解决方案:

1.避免循环更新,优化为一条where锁定要更新的记录批量更新
2.如果非要循环更新,尝试取消事务(能接受的话),即每一条更新为一个独立的事务

2.gap lock/next keys lock导致死锁


死锁原因分析:

1.事务1执行delete age = 27,务2执行delete age = 31,在RR级别,操作条件不是唯一索引时,行锁会升级为next keys
lock(可以理解为间隙锁),因此事务1锁住了25到27和27到29的区间,事务2锁住了29到31的区间
2.事务1执行insert age = 30,等待事务2释放锁
3.事务2执行insert age = 28,等待事务1释放锁
4.死锁产生,死锁日志显示lock_mode X locks gap before rec insert intention waiting

解决方案:

1.降低事务隔离级别到Read Committed,该隔离级别下间隙锁降级为行锁,可以减少死锁发生的概率
2.避免这种场景- -

3.index merge导致死锁

t_user结构改造为:死锁分析:

1.在符合场景前提的情况下(即表数据量较大,index_merge未关闭),通过explain分析update t_user where zone_id = 1 and uid = 1可以发现type是index_merge,即会用到zone_id和uid两个索引
2.上锁的过程为:

事务1:
① 锁住zone_id=1对应的间隙锁: zoneId in (1,2)
② 锁住索引zone_id=1对应的主键索引行锁id = [1,2]
③ 锁住uid=1对应的间隙锁: uid in (1, 2)
④ 锁住uid=1对应的主键索引行锁: id = [1, 3]

事务2:
① 锁住zone_id=2对应的间隙锁: zoneId in (1,2)
② 锁住索引zone_id=2对应的主键索引行锁id = [3,4]
③ 锁住uid=2对应的间隙锁: uid in (1,2)
④ 锁住uid=2对应的主键索引行锁: id = [2, 4]

1.如果两个事务上锁的顺序相反,则有一定的概率出现死锁。另外,index_merge的形式锁住了很多不符合条件的行,浪费了资源。一般死锁日志打印的信息为:lock_mode
X locks rec but not gap waiting Record lock
解决方案:创建联合索引,使执行计划只会用到一个索引。

注:
update table set name = “wea” where col_1 = 1 or col_2 = 2 ;
col_1和col_2为联合索引,遵循最左原则col_1会走索引,但col_2会对整个索引进行扫描,此时会对整个索引加锁。

4.唯一索引冲突导致死锁

死锁分析:

1.三个事务分别尝试插入uid=1的数据,其中事务1先于后两个事务
2.由于是唯一索引,所以后两个事务会出现唯一键冲突,但是事务1并未立即提交,因此不会报错,而是将事务一insert的隐式锁升级为显式锁
3.事务二和事务三为了判断是否出现唯一键冲突,必须进行一次当前读(select…lock in share mode),加的锁是GAP S锁,所以进入阻塞,等待事务一释放锁
4.事务一回滚,此时事务二和事务三成功获取记录上的GAP S锁,并继续执行插入操作
5.插入则需要依次请求插入意向锁,而插入意向锁和GAP S锁冲突,因此两个事务相互等待,形成死锁

解决办法:尽量避免这种插入又回滚的场景。

总结

避免死锁的原则:

建立合适的索引,减小锁的粒度
选择合适的事务隔离级别
大事务拆成小事务,一个事务中的锁尽量少

mysql 死锁分析相关推荐

  1. java mysql死锁_记一次线上mysql死锁分析(一)

    记录一次比较诡异的mysql死锁日志.系统运行几个月来,就在前几天发生了一次死锁,而且就只发生了一次死锁,整个排查过程耗时将近一天,最后感谢我们的DBA大神和老大一起分析找到原因. 诊断死锁 借助于我 ...

  2. 在线分析mysql死锁详解_记一次线上mysql死锁分析(一)

    记录一次比较诡异的mysql死锁日志.系统运行几个月来,就在前几天发生了一次死锁,而且就只发生了一次死锁,整个排查过程耗时将近一天,最后感谢我们的DBA大神和老大一起分析找到原因. 诊断死锁 借助于我 ...

  3. mysql死锁分析工具show engine innodb status

    参考文章 <记录一次MySQL死锁的分析与解决过程> <mysql之show engine innodb status解读> <把MySQL中的各种锁及其原理都画出来&g ...

  4. 两个小工具,MySQL死锁分析,新技能又Get!!!

    数据库死锁,是最难调试与追踪的. 场景如下: 同一个表,事务内先插入一条记录,再更新这条记录,并发时会死锁. 并且能够复现. 可以通过什么工具模拟并发事务,查看信息,解决问题呢?这是今天要分享的内容. ...

  5. 线上MySQL死锁分析——索引设置不当导致的死锁

    文章目录 1. 背景 2. MySQL InnoDB的锁机制 2.1 MySQL中的锁类型 2.2 行锁的加锁规则 2.3 死锁检测机制 3. 本文案例分析 3.1 分析InnoDB status日志 ...

  6. mysql死锁分析_MySQL死锁分析

    熟悉或者了解数据库的朋友都知道锁的概念,这里不做过多的解析!锁的种类有很多,不同数据库的锁管理方式也不同.这里主要谈下MySQL innodb引擎下的死锁. 死锁通俗的来讲就是2个事务相互请求对方持有 ...

  7. MYSQL死锁之路 - 常见SQL语句的加锁分析

    这篇博客将对一些常见的 SQL 语句进行加锁分析,看看我们平时执行的那些 SQL 都会加什么锁.只有对我们所写的 SQL 语句加锁过程了如指掌,才能在遇到死锁问题时倒推出是什么锁导致的问题.在前面的博 ...

  8. MySQL死锁解决之道

    转载自知乎 云之飞舞 真大佬 一. 了解常见的锁类型 在讨论传统的隔离级别实现的时候,我们就提到:通过对锁的类型(读锁还是写锁),锁的粒度(行锁还是表锁),持有锁的时间(临时锁还是持续锁)合理的进行组 ...

  9. mysql 死锁监控_mysql 死锁

    MySQL复制slave服务器死锁案例 原文:MySQL复制slave服务器死锁案例 MySQL复制刚刚触发了一个bug,该bug的触发条件是slave上Xtrabackup备份的时候执行flushs ...

最新文章

  1. poj 2449 Remmarguts' Date 启发式搜索 A*算法
  2. 个人在iOS开发过程中真机调试遇到的问题及解决方法
  3. P4245 【模板】任意模数NTT
  4. 【JavaSE03】Java中分支语句-思维导图
  5. .以及JDK1.5ConcurrentHashMap新特性
  6. java系统架构师有的特质_Java中特质模式的定义
  7. CentOS 7 Linux 的初始化系统(系统服务管理和控制程序/Init System) -- systemd 详解
  8. dup和dup2的使用方法
  9. easyPR源码解析之chars_identify.h
  10. 20175204 张湲祯 2018-2019-2《Java程序设计》第三周学习总结
  11. python菱形_python如何输出菱形与空心菱形详解与巧妙地使用center方法
  12. php单例模式代码,php设计模式之单例模式代码
  13. Linux入门学习教程:在Ubuntu 14.04中安装使用搜狗拼音输入法
  14. 学生健康管理软件/中小学体检数据管理系统
  15. 计算机不能报名系统软件,电脑无法安装税控系统、国税申报软件,怎么回事
  16. Unity UGUI实现王者荣耀版多格血条
  17. 申请实用新型专利的流程
  18. 对待事物,乐观积极。
  19. 图片翻译软件哪个好用?这些软件值得收藏
  20. 云南省计算机一级b试题及答案,计算机一级b模拟试题及答案

热门文章

  1. 进军python——与@龟叔握手
  2. html个人主题制作,【Html】pelican主题的制作之模板
  3. 重罚超8亿!范冰冰“阴阳合同”逃税案终于水落石出!
  4. 面试 什么场景应该拆分系统,什么场景应该合并系统
  5. 机器学习中的数学——激活函数(四):Leaky ReLU函数
  6. 数值模拟udec软件学习第一周
  7. “要爽由自己,冰火暴风城”活动动态
  8. MATLAB图像数据集的制作,使用labelImg制作自己的数据集(VOC2007格式)用于Faster-RCNN训练...
  9. 在CentOS下安装MySQL
  10. 解决windows共享打印机连接失败