引言
所谓死锁:是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去. 此时称系统处于死锁状态或系统产生了死锁, 表级锁不会产生死锁. 所以解决死锁主要还是针对于最常用的InnoDB。
死锁产生条件
虽然进程在运行过程中,可能发生死锁,但死锁的发生也必须具备一定的条件,死锁的发生必须具备以下四个必要条件:
1)互斥条件: 指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。
2)请求和保持条件: 指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。
3)不剥夺条件: 指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。
4)环路等待条件: 指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源。
我们强调所有四个条件必须同时成立才会出现死锁。环路等待条件意味着占有并等待条件,这样四个条件并不完全独立。
死锁的关键在于:2+ 的 session 加锁的顺序不一致。
那么对应的解决死锁问题的关键就是:让不同的 session 加锁有次序。
死锁原因:隔离级别、GAP锁、索引的顺序及业务逻辑
-
尽量使用较低的隔离级别,比如如果发生了间隙锁,你可以把会话或者事务的事务隔离级别更改为 RC(read committed)级别来避免,但此时需要把 binlog_format 设置成 row 或者 mixed 格式
-
精心设计索引,并尽量使用索引访问数据,使加锁更精确,从而减少锁冲突的机会;
-
选择合理的事务大小,小事务发生锁冲突的几率也更小;
-
给记录集显示加锁时,最好一次性请求足够级别的锁。比如要修改数据的话,最好直接申请排他锁,而不是先申请共享锁,修改时再请求排他锁,这样容易产生死锁;
-
不同的程序访问一组表时,应尽量约定以相同的顺序访问各表,对一个表而言,尽可能以固定的顺序存取表中的行。这样可以大大减少死锁的机会;
一、不同表相同记录行锁冲突
这种情况很好理解,事务A和事务B操作两张表,但出现循环等待锁情况
![](https://img-blog.csdnimg.cn/img_convert/1e4b62ed6b9a4e838cba846e3375fd21.png)
二、相同表记录行锁冲突
这种情况比较常见,之前遇到两个job在执行数据批量更新时,jobA处理的的id列表为[1,2,3,4],而job处理的id列表为[8,9,10,4,2],这样就造成了死锁。
![](https://img-blog.csdnimg.cn/img_convert/f3625161e60745dbb60c468737849412.png)
三、gap锁冲突
innodb在RR级别下,如下的情况也会产生死锁,比较隐晦。不清楚的同学可以自行根据上节的gap锁原理分析下。
![](https://img-blog.csdnimg.cn/img_convert/270331d88e2a4be79b3adf5151c8fb03.png)
四、不同索引锁冲突
这种情况比较隐晦,事务A在执行时,除了在二级索引加锁外,还会在聚簇索引上加锁,在聚簇索引上加锁的顺序是[1,4,2,3,5],而事务B执行时,只在聚簇索引上加锁,加锁顺序是[1,2,3,4,5],这样就造成了死锁的可能性。
![](https://img-blog.csdnimg.cn/img_convert/5e25f2ae52924a1cb8843197e2bcba57.png)
五、死锁检测
wait-for graph原理
我们怎么知道上图中四辆车是死锁的?他们相互等待对方的资源,而且形成环路!我们将每辆车看为一个节点,当节点1需要等待节点2的资源时,就生成一条有向边指向节点2,最后形成一个有向图。我们只要检测这个有向图是否出现环路即可,出现环路就是死锁!这就是wait-for graph算法。
六、死锁检测命令
#1、查看正在进行中的事务
SELECT *
FROM information_schema.INNODB_TRX
#2、查看正在锁的事务
SELECT *
FROM INFORMATION_SCHEMA.INNODB_LOCKS;
#3、查看等待锁的事务
SELECT *
FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;
#4、查询是否锁表
SHOW OPEN TABLES
WHERE In_use > 0;
#在发生死锁时,这几种方式都可以查询到和当前死锁相关的信息。
#5、查看最近死锁的日志
SHOW ENGINE innodb STATUS