@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的源码翻下实现原理,到底为啥出现这个问题。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。


最新评论