补偿类job
。补偿类job典型的特点是带有‘status’状态,比如:正常业务status应该从‘init’–>‘processing’–>‘sucess’,但是如果数据库中一直是init,说明该数据一直未被处理(原因可能之前处理的时候失败了,所以一直是init)。此时,补偿类会把init状态的数据查询到并进行处理:
数据迁移类job
。数据迁移job不需要‘status’状态,是完全的把A表数据复制到B表。 值得注意的是,如果只是做数据迁移且不涉及到数据的处理和聚合,那么迁移这个操作可以交给数仓来处理。补偿类job、数据迁移类job
)补偿类job、数据迁移类job
)补偿类job、数据迁移类job
)数据迁移类job
)数据迁移类job
)补偿类job、数据迁移类job
)补偿类job、数据迁移类job
)数据迁移类job
)此类job的特点是:耗时短、数据量较少、带有status状态。
需要额外注意两点:
status转换
问题,可能造成job重复拉取
执行。当我把状态是init的查询出来后,在handle中做了处理,那么处理成功、失败后,该init状态是否需要转换为sucess、fialed,还是说依旧保持init。因为这关系到我下一次job执行是否还会再次处理该条数据。时间限制
,对一直处理失败的数据进行放弃
。因为确实存在这种情况:某几条状态是init的数据始终是处理失败。对于这种情况,我们需要添加时间限制,比如限制只查询前60分钟~10分钟的、状态是init的数据。 public void schedule() {
log.info("[start.");
/* 分布式锁解决并发执行问题 */
RLock lock = redissonClient.getLock("keyLock");
if (!lock.tryLock()) {
log.info("lock conflict");
return;
}
log.info("distribute lock success.");
try {
/* 开关。是否开启 */
if (switch) {
handler();
}
}catch (Exception exception){
log.error("fail.", exception);
}finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
log.info("end.");
}
private void handler() {
log.info("handler start.");
long amountCheckStart = 60 * 60 * 1000;
long amountCheckEnd = 10 * 60 * 1000;
AssertUtil.isTrue(amountCheckStart > amountCheckEnd, "时间区间设置错误");
Date curDate = new Date();
// 示例: 60分钟前的时间
Date startTime = new Date(curDate.getTime() - amountCheckStart);
// 示例: 10分钟前的时间
Date endTime = new Date(curDate.getTime() - amountCheckEnd);
Long requestQueryId = 0L;
// 滚动查询查询数据
List<RequestDO> requestList =
aigcRequestRepository.queryRolledRequestList(requestQueryId, startTime, endTime);
log.info(" requestList.size:[{}]", requestList.size());
/* 扫表 */
while (!CollectionUtils.isEmpty(requestList)) {
for(int i = 0 ; i < requestList.size(); i++) {
RequestDO request = requestList.get(i);
Long id = request.getId();
String requestId = request.getRequestId();
String requestStatus = request.getRequestStatus();
String statusExplain = request.getStatusExplain();
try {
log.info("[check amount] 开始校对. id:[{}], requestId:[{}], status:[{}], statusExplain:[{}]",
id, requestId, requestStatus, statusExplain);
/* 核心业务处理 */
doHandler(request.getUserId(), requestId, requestStatus, statusExplain);
log.info("[check amount] 结束校对. id:[{}], requestId:[{}], status:[{}], statusExplain:[{}]",
id, requestId, requestStatus, statusExplain);
}catch (Exception exception) {
log.error("[check amount] 权益校对失败. id:[{}], requestId:[{}], status:[{}], statusExplain:[{}]",
id, requestId, requestStatus, statusExplain, exception);
}
}
// 获取下次查询的游标
requestQueryId = requestList.get(requestList.size() - 1).getId();
requestList = aigcRequestRepository.queryRolledRequestList(requestQueryId, startTime, endTime);
log.info("[check amount] requestList.size:[{}]", requestList.size());
}
log.info("[check amount] handler end");
}
}
/**
* SQL示例:
* select * from request where id > 6 and
created_time between '2023-03-24 00:00:00' and '2023-03-24 23:00:00' and status in () order by id asc limit 100 ;
* @param id
* @param startTime
* @param endTime
* @return
*/
public List<RequestDO> queryRolledRequestList (Long id, Date startTime, Date endTime){
LambdaQueryWrapper<RequestDO> queryCondition = new QueryWrapper().lambda();
queryCondition.eq(RequestDO::getDeleted, 0);
queryCondition.eq(RequestDO::getRequestStatus, RequestRecordStatus.PROCESSING.getCode());
queryCondition.or().in(RequestDO::getStatusExplain,
Lists.newArrayList(RequestStatusExplain.OCCUPY_UN_KNOW_ERROR.getCode(),
RequestStatusExplain.RELEASE_UN_KNOW_ERROR.getCode()));
queryCondition.between(RequestDO::getCreatedTime, startTime, endTime);
queryCondition.gt(RequestDO::getId, id);
queryCondition.orderByAsc(RequestDO::getId);
queryCondition.last("limit 100");
return aigcRequestDAO.selectList(queryCondition);
}
此类job的特点是:耗时长、可能会中断所以需要续跑、全表无差别复制。
注意事项:
https://gitee.com/MyFirstMyYun/spring-boot-project-case/blob/master/job-scan-table-demo/src/main/java/com/xiaolyuh/controller/HistoryEventTrackingTask.java
https://gitee.com/MyFirstMyYun/spring-boot-project-case/blob/master/job-scan-table-demo/src/main/java/com/xiaolyuh/controller/SingleTableScanByNoAutoFieldJob.java
略。