前言:
最近项目上压测出现了数据库死锁情况,经过一番排查,最终定位在updateData方法上
分析该方法:
@Transactional(propagation = Propagation.REQUIRES_NEW)
public int udpateData(DataTrace trace){
//根据索引更新数据,该子方法也加了@Transactional注解,使用默认事务机制:required
updateOtherByIndex(trac);
//根据主键更新数据
updateOtherByPrimary(trac);
}
1.该方法执行了两个更新sql,一个根据索引,一个根据主键
2.外层调用udpateData方法,存在这种情况:首先异步调用,然后马上同步调用
分析死锁原因:
1.首先udpateData方法存在事务嵌套,子方法updateOtherByIndex使用默认事务机制required,会将已存在的事务加入当前事务中,所以两个更新sql是在一个事务中
2.既然没有开启一个新事务,则两个更新sql在一个事务中,并发调用udpateData方法就可能会出现问题(而且也是巧,第一个请求不会调用子方法updateOtherByIndex):
第一个进来事务会执行updateOtherByPrimary方法,获取主键锁
第二个进行事务会执行updateOtherByIndex方法,获取索引锁,然后执行updateOtherByPrimary方法,获取主键锁
这种循环情况下在压测环境很容易出现死锁了…
虽然原因找到了,不过为了更好地理解和加深学习,我本地做了一个死锁测试:
一、前置条件:
1.创建表:
CREATE TABLE `test_deadlock_table` (
`primary_no` char(16) NOT NULL COMMENT '主键号',
`index_no` char(16) NOT NULL COMMENT '索引号',
`status` tinyint(4) unsigned NOT NULL COMMENT '状态',
PRIMARY KEY (`primary_no`),