死锁案例
Ex in ******: org.springframework.dao.DeadlockLoserData*****#*#***** ### Error updating data*****#*#***** ql.SQLTransactionRollbackException: (conn=180366245) Deadlock found when trying to get lock; try restarting transaction[N]### The error may exist in file.....
查看告警是死锁导致,分析这段代码逻辑如下:
@Transactional(rollbackFor = Exception.class)
public void handleOneBatch(List<AccumulatorTmpFeeEntry> mainFeeTempList, AccumulatorFeeMatchKey batchKey) {
// 业务操作
doBusiness(...);
// dao1的查询操作
dao1.select(....);
// 业务操作
doBusiness(...);
// 其他表的DML操作
dao2.insert(....);
// 业务操作
doBusiness(...);
// 其他表的DML操作
dao2.update(....);
// dao1的删除操作
dao1.delete(....);
}
异常点出现在dao1.delete(....);
初步推测是由于select操作加S锁,然而事务中存在大量的业务逻辑,导致单个事务耗时长,此时事务2也执行select操作加S锁,这是事务1执行dao1.delete()
操作申请X锁,则申请不到,产生死锁
但是这个推测有一点站不住脚:
dao1.delete操作使用了主键索引,方法为range,应该可以使用行锁,只要两个事务处理的不是同一批数据,就应该不会导致锁竞争的问题,而方法上游是有redis加锁机制的
进一步分析,如果处理的不是同一批数据就不会竞争吗?
查阅资料发现,加锁的原理实际是对索引加锁。InnoDB 的行锁是通过给索引上的索引项加锁来实现的。
如果使用的是主键索引,则直接对主键索引加锁;
如果使用的是二级索引,先对二级索引加锁,然后再对主键索引加锁(因为要回表);
不使用索引,则是全表加锁
且使用的是nextkey锁(行锁+GAP锁),即对一条数据及其一个范围的GAP加锁,可能导致不同数据出现冲突
但是再次查询数据库的隔离级别:READ COMMITTED,这个级别应该是使用MVCC限制读,不加nextkey锁,这一点依旧存疑
于是继续在网上找资料,偶然找到一篇wiki:https://blog.csdn.net/n88Lpo/article/details/127032963
其中分析的是在一些insert场景下,RC级别也可能加nextkey锁,因此考虑此场景是否有insert与delete并发的场景,由于目前没有拿到死锁的样本,计划下次发生死锁时执行show engine innodb status
捞取一些详细的加锁信息查看
评论区