根本:数据库中的死锁问题,其实是由于间隙锁和插入意向锁不兼容
如何导致的死锁:
两个事务,事务A先获取相同区间的间隙锁,然后事务B也获取相同区间的间隙锁,这是两个事务都持有相同的间隙锁,间隙锁是不冲突的(间隙锁的意义只在于阻止区间被插入,因此是可以共存的。一个事务获取的间隙锁不会阻止另一个事务获取同一个间隙范围的间隙锁),但是两个事务不能同时获取一段位置的间隙锁和插入意向锁。
此时,事务A插入数据或者别的操作,检测到当前位置有间隙锁,会生成插入意向锁,锁的状态设置为等待状态,等待B的间隙锁释放;事务B插入数据,也检测到当前位置有间隙锁,也会生成插入意向锁,等待A的间隙锁释放,此时就发生了死锁。
解决办法:
死锁的四个必要条件:互斥、占有且等待、不可强占用、循环等待。只要系统发生死锁,这些条件必然成立,但是只要破坏任意一个条件就死锁就不会成立。
在数据库层面,有两种策略通过「打破循环等待条件」来解除死锁状态:
- 设置事务等待锁的超时时间。当一个事务的等待时间超过该值后,就对这个事务进行回滚,于是锁就释放了,另一个事务就可以继续执行了。在 InnoDB 中,参数 innodb_lock_wait_timeout 是用来设置超时时间的,默认值时 50 秒。
- 开启主动死锁检测。主动死锁检测在发现死锁后,主动回滚死锁链条中的某一个事务,让其他事务得以继续执行。将参数 innodb_deadlock_detect 设置为 on,表示开启这个逻辑,默认就开启。