java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > @Transactional引发的数据库死锁

@Transactional引发的数据库死锁问题及解决

作者:熬夜的王

生产代码中updateById引发数据库死锁?别慌,这篇实战复盘教你快速定位长事务超时根源,从DBA反馈到@Transactional注解陷阱,我们一步步拆解解决方案,帮你避免类似生产事故

生产事故往往出现在一个不经意间的代码中产生的。

今天在生产代码中发生了数据库死锁的情况。操作由于调用updateById(Entity entity)的方法产生的。

Cause: com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException: Lock wait timeout exceeded; try restarting transaction
    at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)
    at org.apache.ibatis.session.defaults.DefaultSqlSession.update(DefaultSqlSession.java:199)
    at com.baomidou.mybatisplus.extension.activerecord.Model.updateById(Model.java:120)

由于是生产数据库,并且在测试环境同一功能未产生该问题。所以反馈给dba排查是否有死锁的SQL语句在执行中,等待dba回复的时间开始排查问题。

假设数据库返回的错误表名称为:table_test

排查过程

1.在生产通过界面或接口调用操作table_test表的数据。

** 结果:** 功能正常,能够正常更新数据。

2.分析代码,接口为post接口,主要代码逻辑如下:

	@Transactional
	public void updateBatchByCode(String code){
		List<Test1> list = service1.getListByCode(code);
		for(Test1 test1 : list){
			test1.setName("name1");
			service1.updateById(test1);
			List<Test2> list2 = service2.getListByTestCode(test1.getCode());
			for(Test2 test2 : list2){
				test2.setCode("code1");
				service2.updateById(test2);
			}
		}
	}

分析代码后觉得没有什么问题,因为代码逻辑比较简单。

这时也收到了DBA的回复:数据库无死锁语句,但是监测到有长时间未提交事务。 因为超过100秒未结束的事务,会被做超时处理。

再次分析代码,这时将目光定位在@Transactional这个注解上,我们知道该注解是用来将方法内的数据库操作放到一个事务中,如果执行过程中有异常进行数据回滚。

换个思路,这个就符合DBA提出的有长事务超时。

解决方案

去除@Transactional注解

去除了注解后,数据事务一致性得不到保障,当然我这里是因为该方法是用于处理特殊业务,所以去除了不要紧。大家在如果遇到该问题,建议:

将代码解耦,不要将很多的更新操作在一个长事务中使用。

后面将Transactional的源码翻下实现原理,到底为啥出现这个问题。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文