You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Quartz加@Transactional后triggerJob触发任务延迟被MisfireHandler处理

问题背景

定时任务逻辑非常简单,仅在控制台输出当前时间,需要实现一个按钮用于立即触发该任务执行。

相关代码

XxxService.class

@Override
@Transactional(rollbackFor = Exception.class)
public void run(Long[] jobIds) {
    for(Long jobId : jobIds){
        ScheduleUtils.run(scheduler, this.getById(jobId));
    }
}

ScheduleUtils.class

public static void run(Scheduler scheduler, ScheduleJobEntity scheduleJob) {
    try {
        JobDataMap dataMap = new JobDataMap();
        dataMap.put(ScheduleJobEntity.JOB_PARAM_KEY, scheduleJob);
        System.out.println(Thread.currentThread()+"need hello..."+new Date());
        scheduler.triggerJob(getJobKey(scheduleJob.getJobId()), dataMap);
    } catch (SchedulerException e) {
        throw new RRException("run fail", e);
    }
}

MyJob(HelloJob)

@Data
@Component("HelloJob")
public class HelloJob extends CustomJob {

    public String jobDescription = "演示Job";

    @Override
    public void run(String params) {
        System.out.println(
                Thread.currentThread() +"hello..."+new Date() + "params: " + params);
    }

}

复现现象

  • 在run(Long[] jobIds)方法上添加*@Transactional*注解时,任务不会立即执行,通常延迟15~20秒才会运行,日志输出如下:
Thread[http-nio-8500-exec-1,5,main]need hello...Mon Jun 27 12:55:02 CST 2022

2022-06-27 12:55:24 INFO  [bear] QuartzScheduler_BearScheduler-DESKTOP-521BOOH1656305645549_MisfireHandler org.springframework.scheduling.quartz.LocalDataSourceJobStore Handling 1 trigger(s) that missed their scheduled fire-time.
Thread[BearScheduler_Worker-3,5,main]hello...Mon Jun 27 12:55:24 CST 2022params: aaaa,bbbbbbb
  • 移除run(Long[] jobIds)方法上的*@Transactional*注解后,任务可正常立即执行,日志输出如下:
Thread[http-nio-8500-exec-4,5,main]need hello...Mon Jun 27 12:59:40 CST 2022
Thread[BearScheduler_Worker-6,5,main]hello...Mon Jun 27 12:59:40 CST 2022params: aaaa,bbbbbbb
原因说明

核心问题是Quartz使用JDBC持久化作业存储(即配置的LocalDataSourceJobStore)时,加了@Transactional后出现事务可见性问题:

  1. 调用scheduler.triggerJob()时,Quartz会直接向数据库插入一条临时触发的任务记录,但因为外层Service方法加了@Transactional,这个插入操作会加入当前方法的事务,不会立刻提交到数据库。
  2. Quartz的调度线程靠定时轮询数据库拉取待执行任务,未提交的事务记录对调度线程不可见,所以它根本感知不到即时触发任务的请求,自然不会马上调度。
  3. 等整个run方法执行完、事务提交时,这条触发记录才真正持久化到数据库。此时距离调用triggerJob已经过去一段时间,Quartz下一次轮询扫到这条记录时,发现记录里的计划触发时间早于当前时间、超过了misfire阈值,就会判定这个任务是“错过触发的任务”,交给MisfireHandler处理,这时候才会真正执行任务,也就是看到的15~20秒延迟。

去掉@Transactional之后,triggerJob触发的数据库插入操作没有外层事务包裹,执行完就立刻提交,Quartz调度线程很快就能扫到这条待执行记录,任务自然就立即运行了。

解决方案
  • 如果run方法里没有其他需要事务保证一致性的数据库写操作,直接移除方法上的@Transactional注解即可,和验证的效果一致。
  • 如果方法里确实有其他必须放在事务里的数据库操作,可以把Quartz任务触发的逻辑挪到事务提交完成之后再执行,比如通过Spring的事务同步管理器注册提交后回调,在回调里调用ScheduleUtils.run触发任务,就能保证触发记录写入数据库后立刻对Quartz可见,不会出现misfire延迟。

内容的提问来源于stack exchange,提问作者HoRinbb

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 08:54:21