今天线上出现了一个诡异bug,部分数据被提前消费。由于业务流程保密问题,下面用例子详解
场景:现在有个需求,学校需要统计每个班,每天的考勤情况
我们用两个定时任务来解决,定时任务A插入考勤数据,定时任务B统计考勤数据
定时任务之间可能存在并发。
package com.test.scholl.service.impl;
// 定时任务A执行方法
public void insertAttendanceAndDetails(Attendance attendance){
// 插入主表
this.insertAttendance(attendance);
// 插入明细表
for(AttendanceDetail detail: attendance.getDetails()){
this.insertDetails(detail);
}
}
public void censusAttendance(){
// 1.查询未统计的班级
// 2.折算出勤比例
// 3.更新主表状态为已统计
}
此时注意,定时任务A中insertAttendanceAndDetails方法存在一个隐患,是啥呢?跑下程序,你会发现,该方法未受事务控制,也就是在执行完主表插入,事务自动提交,执行每条子表插入,事务自动提交。此时,如果定时任务B正好消费到定时任务A已提交数据,但数据未全的情况下,就会引发部分数据被提前消费,剩余数据永远不会被消费的问题。
为啥会没受事务控制呢?
我们首先调用的是AOP代理对象而不是目标对象,首先执行事务切面,事务切面内部通过TransactionInterceptor环绕增强进行事务的增强,即进入目标方法之前开启事务,退出目标方法时提交/回滚事务。
而insertAttendanceAndDetails方法里的this指向目标对象,因此调用this.insertDetails()将不会执行insertDetails事务切面,即不会执行事务增强
附上解决链接,供参考
https://www.jianshu.com/p/8ebcbacd9657