spring声明式事务未提交引发的线上问题
1、现象
接收到kafka传来的b类信息(分a,b两类,b类的一些处理依赖a类)处理异常邮件,处理后,发现有一个pod日志打印数据库连接失败,重启后,某业务监控页面一直告警,并且两个pod都停止消费文件,都重启后,恢复正常
2、根因分析
实际重启pod,也只是临时解决问题,并未解决根因,还会出现相似问题,所以要分析根因并解决
1)找到DBA查看数据库使用情况,发现相关表出现阻塞的情况,导致无法处理文件
2)排查代码问题,发现几处业务代码使用手动事务时,在特殊情况无法关闭
3)第一处xxljob触发的定时任务A,每日执行,只会触发pod1
DefaultTransactionDefinition def =new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
TransactionStatus transactionStatus = transactionManager.getTransaction(def);
try{if (arrayList.size() > 0) {// 执行业务transactionManager.commit(transactionStatus);}
} catch(Exception e){log.error(" " ,e);transactionManager.rollback(transactionStatus);
}
当arrayList为空时,永远无法释放资源,所以在一定时间后,该pod无法连接数据库
4)第二处是处理Kafka信息的代码,由于pod1无法连接数据库,导致一些a类信息在pod1无法处理,而相关的b类消息被分配到pod2时,运行如下代码,会无法释放数据库连接
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
TransactionStatus transactionStatus = transactionManager.getTransaction(def);
Orders order = 查询语句;
if (Objects.isNull(order)) {try {//业务代码transactionManager.commit(transactionStatus);return orderData;} catch (Exception e) {transactionManager.rollback(transactionStatus);throw new BusinessException(e.getMessage());}
}
5)在4)中的spring线程会在一定时间后被收回,而这个线程中存在未提交的事务,而消费文件的代码会获取到该类线程,此处代码有手动在数据库加锁、解锁的业务,这会导致相关表被阻塞,导致无法消费文件
3、解决方法
1)编程式事务改为声明式
2)增加数据库连接
3)增加监控
4)增加手动处理文件接口
参考文章:MySQL中Spring管理的事务开启后不提交引起的事故_spring开启了一个事物不提交也不回滚会产生问题吗-CSDN博客