一、死锁是如何产生的?
死锁是多个进程或线程因竞争有限的资源而发生的一种相互等待的状态,使得每个进程或线程都无法继续执行。
死锁产生的条件包括:
互斥条件:至少有一个资源是独占的,即一次只能被一个进程或线程使用。
持有和等待条件:一个进程或线程可以持有一个资源,并等待其他进程或线程持有的资源。
非抢占条件:已经分配给一个进程或线程的资源不能被强制性地抢占,只能由持有资源的进程或线程显式释放。
循环等待条件:一系列进程或线程形成循环等待其他进程或线程持有的资源。
二、死锁的排查思路
用show engine innodb status,查看最近一次死锁日志。
分析死锁日志,找到关键词TRANSACTION
分析死锁日志,查看正在执行的SQL
看它持有什么锁,等待什么锁。
为了进一步验证,可以通过这个命令(MySQL 8.0+)查看SQL加锁情况:
SELECT * FROM performance_schema.data_locks\G;
可以得出结果,INSERT语句的时候,持有了唯一索引的排他行锁,然后DELETE的时候,也需要获取这个锁,因此形成死锁循环等待。
发现当执行删除SQL的时候,会给唯一索引 idx_unique_flow_no加一个排他锁。那就奇怪了,我们从刚才日志可以发现,插入SQL等待的是一个idx_unique_flow_no的读共享锁。为啥会冲突呢?其实是因为 读共享锁跟排他锁是冲突的:
可以得出结果,delete语句的时候,持有了唯一索引的排他行锁,然后insert的时候,也需要获取这个索引的读共享锁,因此形成死锁。
三、死锁解决方案
因为并发执行删除和插入同一个表,因此形成死锁。
解决死锁的方案有:
1、避免循环等待:保证资源分配的有序性,例如,定义一个全局的资源申请顺序,并要求所有进程按照这个顺序申请资源。这样可以避免循环等待的情况。
2、资源有序性:按照固定的顺序获取资源,避免多个进程在不同的顺序下请求资源,导致循环等待的情况。
3、超时机制:当一个进程无法获取所需资源时,设置一个超时机制,超过一定时间后放弃等待的资源并释放自己所持有的资源,避免长时间等待。