今天在测试线程池死锁时,想起来当初在公司中遇到的一次数据库死锁经历;
1.起因是有一天我看服务器日志,出现了死锁日志,并说明情况以前就发生过,不过概率很小;
2.定位出现死锁问题代码,发现是mq消费端,其中大致的业务是(消费一个消息,该消息会携带多个商品id及处理方式,然后需要for循环进行查询更新操作)
3.于是想问题出现原因,因为数据库使用的mysql的innodb引擎,所以在处理更新数据时会使用到行级的排他锁
(1)想到是否是因为处理速度太慢,引发锁等待超时?(测试加日志分析其实处理并不慢,达不到超时的时间)
(2)想到是否由于循环依赖锁资源产生;
4.查看业务和测试得出的却是上述第二种情况:原因就是
当并发的接受消息后,并发处理期间,出现了 1事务中更新A 然后更新B操作,此时2事务中 更新B 更新A操作;
此时如果正好1事务在更新A后,2事务更新B,那么A B都占有锁资源,随后1事务想要更新B,2事务想要更新A,那么发生死锁;
5.查明问题原因后就好解决了:
(1)最简单快速方案,将消费消息的商品顺序进行排序,那么就会避免出现循环依赖死锁问题,只可能出现等待锁超时问题;
(2)将mq消费线程设置为1,但是消费能力肯定下降,要依照业务需求来定;
(3)使用分布式锁方式,来禁止相同商品并发消费,也是依照业务尝试使用;
(4)重构业务代码,将整体事务缩小执行时间,避免出现死锁;