mysql insert 时出现Deadlock死锁场景分析

案例一:
有一张表A,先更新,如果影响行数为0,则执行INSERT插入数据。很常见的场景,在生产上也跑了很久,没有出现什么问题。但是有一次在测试环境做压测时居然出现了死锁,Deadlock found when trying to get lock; try restarting transaction
在这里插入图片描述

因为对mysql锁不熟悉,为什么insert也会死锁,不是一般在update的时候会死锁吗? 很好奇,于是开始寻找原因…

mysql锁是跟数据库设置的隔离级别有关系的,不同的隔离级别,锁也各不相同,只要是为了解决类似脏读、幻读、可重复读的问题。
select @@tx_isolation; – 查看隔离级别
在这里插入图片描述

隔离级别是RR(可重复读),我们知道,在此隔离级别下,为了解决幻读的问题,会有存在间隙锁和Next-key lock(行锁 + 间隙锁),即在update、delete语句后会产生间隙锁和Next-key lock,如果在并发下,存在两个事务都事前执行了UPDATE语句(各自持有了gap lock),当INSERT时,要先在插入间隙上获取插入意向锁,由于插入数据的间隙存在冲突,所以会互相等待获取插入意向锁,即相互竞争,最终会导致一方死锁。

这也解释了为什么在测试环境出现死锁。
解决:

  1. 不采用事务,即无事务方式执行,但是如果出现异常则会出现数据不一致的情况。
  2. 调整事务隔离级别为read commit,RC级别不会产生gap lock

由于我们都知道事务的重要性,所以选择第二种方式,将隔离级别改为RC。我们在生产环境一般也就这个级别,所以也解释了为什么在生产没有出现这个问题。

事务隔离级别调整可以通过spring的声明式事务方式来实现,通过注解@Transactional

@Transactional(rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
public void calculationEntry(List<CampaignFeedback> campaignFeedbackList, String groovyScript) {
    
    
    //todo
}

再次压测,死锁问题不再出现。

案例二:
由于业务需要,经常对某张表做更新或者写入,又不想每次都先查询是否存在再插入,所以使用了mysql的replace into来实现。
replace into 语法是如果表中存在主键或者唯一索引,它会按这个维度去判断是否存在,如果存在则会先delete在insert,即它会分开两步来操作,不保证原子性。

开始使用后觉得还是很方便的,毕竟不用自己去多查询一次,mysql自动帮我们完成了。 但是到了压测环节,死锁竟然又出现了。。。。。。

因为有了前面的经验,所以这都不是问题,于是按之前的方式改了。在测试环境跑了几次都没问题,以为都是一样的问题导致,所以就上线了。

上线后,当我去验证这个功能的时候,尼玛。。。死锁问题再一次出现在我的眼前。
在这里插入图片描述
我很慌,为什么这次不行,难道我代码被覆盖了? 翻看git的提交记录并没有。。

于是我们去咨询了DBA,他说replace into 这个不建议使用,因为在大并发环境下,肯定会出现死锁。建议我们使用 insert into … values … on duplicate key update … 来替代

于是按DBA建议改了,再次上线后,不管怎么并发都不会出现死锁。
原因分析,如果使用replace into mysql会默认开启Gap lock和Next-key lock,所以即使加了事务配置也不起作用。

最后附上 insert into … on duplicate key update 介绍:
语法:
INSERT INTO tablename (field1,field2, field3, …) VALUES(value1, value2, value3, …) ON DUPLICATE KEY UPDATE field1=value1,field2=value2, field3=value3, …;

也可以使用以下方式(这种支持批量插入)
INSERT INTO TABLE (a,b,c) VALUES
(1,2,3),
(2,5,7),
(3,3,6),
(4,8,2)
ON DUPLICATE KEY UPDATE a=VALUES(a), b=VALUES(b),c=VALUES(c);

猜你喜欢

转载自blog.csdn.net/huangdi1309/article/details/105663466