记一次mysql死锁问题
场景:innodb下不同的事务进行更新和插入操作导致数据库死锁,代码如下,在批量插入之前,进行了逻辑删除操作,这段代码在并发情况下出现死锁



代码分析
activityId为外键,有外键索引,如果是行级锁肯定不会出现死锁,所以更新的时候肯定不止锁了一条数据
代码中更新操作的是非唯一索引列在innoDB引擎下会触发 next-key lock(间隙锁)。
举例: 表t中有非唯一索引列 test_id为 1, 10, 18, 22, 26的5条数据,此时模拟操作:
事务A 删除一条不存在的数据,数据库就会去找从左开始找最近的索引值
delete from t where test_id= 27;
事务B 删除一条不存在的数据,数据库就会去找从左开始找最近的索引值
delete from t where test_id= 28;
此时事务A和B就会分别产生一个(26,正无穷)间隙锁,然后继续操作
事务A
INSERT INTO t VALUES(27);
此时事务A阻塞,因为事务B在删除操作时拥有了区间锁
事务B
INSERT INTO t VALUES(28);
此时事务B就会死锁,因为事务A在删除操作时拥有了区间锁
解决之道
1、删除时先判断数据是否存在
2、删除和插入分两个事务处理
3、将事务隔离级别设置为读已提交
如果更新数据库存在数据就不会出现死锁,在DELETE FROM ... WHERE ... 和 UPDATE ... WHERE ... 在搜索遇到的每条记录上设置一个独占的间隙锁。 如果通过索引搜索到唯一行就会产生一个索引记录锁。
INSERT 语句对插入的行设置排他(独占)锁。这个锁是一个索引记录锁,而不是间隙锁,并且不会阻止其他会话在插入的行之前区间中插入数据。
死锁,innoDB检测到会剔除一个事务回滚,让另外一个事务完成。