当前位置: 首页 > news >正文

【任务调度】Quartz入门

Quartz 入门

代码仓库地址:

  • GitHub:chenmeng-test-demos/demo8-task at master · cmty256/chenmeng-test-demos
  • Gitee:demo8-task · chenmeng/chenmeng-test-demos - 码云 - 开源中国

基本介绍

Quartz 是一个开源的作业调度框架,它完全由 Java 写成。

Quartz 自带了集群方案。它通过将作业信息存储到关系数据库中,并使用关系数据库的行锁来实现执行作业的竞争,从而保证多个进程下,同一个任务在相同时刻,不能重复执行。

在 Quartz 体系结构中,有三个组件非常重要:

  1. Scheduler :调度器
  2. Trigger :触发器
  3. Job :任务

Quartz 分成【单机模式】和【集群模式】。在生产环境下,一定一定一定要使用 Quartz 的集群模式,保证定时任务的高可用。

JobStore 的存储方式

有两种:

  • RAMJobStore:将 scheduler 存储在内存中,但是重启服务器信息会丢失。
  • JDBCJobStore:将 scheduler 存储在数据库中。
类型优点缺点
RAMJobStore不要外部数据库,配置容易,运行速度快因为调度程序信息是存储在被分配给 JVM 的内存里面,所以,当应用程序停止运行时,所有调度信息将被丢失。另外因为存储到 JVM 内存里面,所以可以存储多少个 Job 和 Trigger 将会受到限制
JDBC 作业存储支持集群,因为所有的任务信息都会保存到数据库中,可以控制事物,还有就是如果应用服务器关闭或者重启,任务信息都不会丢失,并且可以恢复因服务器关闭或者重启而导致执行失败的任务运行速度的快慢取决与连接数据库的快慢

Quartz 单机入门示例

引入依赖

<dependencies><!-- 实现对 Spring MVC 的自动化配置 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- 实现对 Quartz 的自动化配置 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-quartz</artifactId></dependency></dependencies>

应用配置文件

spring:application:name:task-quartz-memory# Quartz 的配置,对应 QuartzProperties 配置类quartz:job-store-type: memory # Job 存储器类型。默认为 memory 表示内存,可选 jdbc 使用数据库。auto-startup: true # Quartz 是否自动启动startup-delay: 0 # 延迟 N 秒启动wait-for-jobs-to-complete-on-shutdown: true # 应用关闭时,是否等待定时任务执行完成。默认为 false ,建议设置为 trueoverwrite-existing-jobs: false # 是否覆盖已有 Job 的配置properties: # 添加 Quartz Scheduler 附加属性,更多可以看 http://www.quartz-scheduler.org/documentation/2.4.0-SNAPSHOT/configuration.html 文档org:quartz:threadPool:threadCount: 25 # 线程池大小。默认为 10 。threadPriority: 5 # 线程优先级class: org.quartz.simpl.SimpleThreadPool # 线程池类型
#    jdbc: # 这里暂时不说明,使用 JDBC 的 JobStore 的时候,才需要配置

创建任务类

/*** 每次 DemoJob01 都会被 Quartz 创建出一个新的 Job 对象,执行任务。** @author chenmeng*/
public class DemoJob01 extends QuartzJobBean {private final Logger logger = LoggerFactory.getLogger(getClass());// 计数器private final AtomicInteger counts = new AtomicInteger();@Resourceprivate DemoService demoService;@Overrideprotected void executeInternal(JobExecutionContext context) throws JobExecutionException {// counts.incrementAndGet() 的值一直为 1,说明每次 DemoJob01 都是新创建的。logger.info("[executeInternal][定时第 ({}) 次执行, demoService 为 ({})]", counts.incrementAndGet(),demoService);}}/*** @author chenmeng*/
public class DemoJob02 extends QuartzJobBean {private final Logger logger = LoggerFactory.getLogger(getClass());@Overrideprotected void executeInternal(JobExecutionContext context) throws JobExecutionException {logger.info("[DemoJob02-executeInternal][我每第10秒执行一次]");}}

新增配置类

@Configuration
public class ScheduleConfiguration {public static class DemoJob01Configuration {/*** 创建 DemoJob01 的 JobDetail Bean 对象** @return JobDetail*/@Beanpublic JobDetail demoJob01() {return JobBuilder.newJob(DemoJob01.class).withIdentity("demoJob01") // 名字为 demoJob01.storeDurably() // 没有 Trigger 关联的时候任务是否被保留。因为创建 JobDetail 时,还没 Trigger 指向它,所以需要设置为 true ,表示保留。.build();}/*** 创建 DemoJob01 的 Trigger Bean 对象。* 其中,我们使用 SimpleScheduleBuilder 简单的调度计划的构造器,创建了每 5 秒执行一次,无限重复的调度计划** @return Trigger*/@Beanpublic Trigger demoJob01Trigger() {// 简单的调度计划的构造器SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5) // 频率 - 每 5 秒。.repeatForever(); // 次数 - 无限重复。// Trigger 构造器return TriggerBuilder.newTrigger().forJob(demoJob01()) // 对应 Job 为 demoJob01.withIdentity("demoJob01Trigger") // 名字为 demoJob01Trigger.withSchedule(scheduleBuilder) // 对应 Schedule 为 scheduleBuilder.build();}}public static class DemoJob02Configuration {/*** 创建 DemoJob02 的 JobDetail Bean 对象** @return JobDetail*/@Beanpublic JobDetail demoJob02() {return JobBuilder.newJob(DemoJob02.class).withIdentity("demoJob02") // 名字为 demoJob02.storeDurably() // 没有 Trigger 关联的时候任务是否被保留。因为创建 JobDetail 时,还没 Trigger 指向它,所以需要设置为 true ,表示保留。.build();}/*** 创建 DemoJob02 的 Trigger Bean 对象。* 其中,我们使用 CronScheduleBuilder 基于 Quartz Cron 表达式的调度计划的构造器,* 创建了每第 10 秒执行一次的调度计划。** @return Trigger*/@Beanpublic Trigger demoJob02Trigger() {//  基于 Quartz Cron 表达式的调度计划的构造器CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("0/10 * * * * ? *");// Trigger 构造器return TriggerBuilder.newTrigger().forJob(demoJob02()) // 对应 Job 为 demoJob02.withIdentity("demoJob02Trigger") // 名字为 demoJob02Trigger.withSchedule(scheduleBuilder) // 对应 Schedule 为 scheduleBuilder.build();}}}

最后启动运行测试。

Quartz 集群入门示例

引入依赖

    <dependencies><!-- 实现对数据库连接池的自动化配置 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><!-- 本示例,我们使用 MySQL --><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId></dependency><!-- 实现对 Spring MVC 的自动化配置 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- 实现对 Quartz 的自动化配置 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-quartz</artifactId></dependency><!-- 单元测试 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies>

应用配置文件

spring:application:name:task-quartz-jdbcdatasource:user: # 这里主要目的是,为了模拟我们一般项目,使用到的业务数据库。url: jdbc:mysql://127.0.0.1:3306/chenmeng_test?useSSL=false&useUnicode=true&characterEncoding=UTF-8driver-class-name: com.mysql.cj.jdbc.Driverusername: rootpassword: quartz: # 这里主要目的是,Quartz 会使用单独的数据库。如果我们有多个项目需要使用到 Quartz 数据库的话,可以统一使用一个,但是要注意配置 spring.quartz.scheduler-name 设置不同的 Scheduler 名字,形成不同的 Quartz 集群。url: jdbc:mysql://127.0.0.1:3306/chenmeng_quartz?useSSL=false&useUnicode=true&characterEncoding=UTF-8driver-class-name: com.mysql.cj.jdbc.Driverusername: rootpassword: # Quartz 的配置,对应 QuartzProperties 配置类quartz:scheduler-name: clusteredScheduler # Scheduler 名字。默认为 schedulerNamejob-store-type: jdbc # Job 存储器类型。默认为 memory 表示内存,可选 jdbc 使用数据库。auto-startup: true # Quartz 是否自动启动startup-delay: 0 # 延迟 N 秒启动wait-for-jobs-to-complete-on-shutdown: true # 应用关闭时,是否等待定时任务执行完成。默认为 false ,建议设置为 trueoverwrite-existing-jobs: false # 是否覆盖已有 Job 的配置properties: # 添加 Quartz Scheduler 附加属性,更多可以看 http://www.quartz-scheduler.org/documentation/2.4.0-SNAPSHOT/configuration.html 文档org:quartz:# JobStore 相关配置jobStore:# 数据源名称dataSource: quartzDataSource # 使用的数据源# Spring Boot 2.6.0+ 中,job-store-type: jdbc 会自动选择 JobStoreTX,无需显式指定 org.quartz.jobStore.class。# 如果指定了,项目运行会报错。
#            class: org.quartz.impl.jdbcjobstore.JobStoreTX # JobStore 实现类driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegatetablePrefix: QRTZ_ # Quartz 表前缀isClustered: true # 是集群模式clusterCheckinInterval: 1000useProperties: false# 线程池相关配置threadPool:threadCount: 25 # 线程池大小。默认为 10 。threadPriority: 5 # 线程优先级class: org.quartz.simpl.SimpleThreadPool # 线程池类型jdbc: # 使用 JDBC 的 JobStore 的时候,JDBC 的配置initialize-schema: never # 是否自动使用 SQL 初始化 Quartz 表结构。这里设置成 never ,我们手动创建表结构。

注意事项

  • Spring Boot 2.6.0+ 中,job-store-type: jdbc 会自动选择 JobStoreTX,无需显式指定 org.quartz.jobStore.class
  • 如果手动指定 class: org.quartz.impl.jdbcjobstore.JobStoreTX,需要注意配置冲突,可能需要禁用 Spring Boot 的自动配置。
  • 最佳实践是依赖 Spring Boot 的自动配置,减少手动干预。

初始化 Quartz 表结构

Quartz 下载

在 Quartz Download 地址,下载对应版本的发布包。

  • 解压后,我们可以在 src/org/quartz/impl/jdbcjobstore/ 目录,看到各种数据库的 Quartz 表结构的初始化脚本。
  • 这里,因为我们使用 MySQL,所以使用 tables_mysql_innodb.sql 脚本。

Quartz 表结构的说明

(详细说明参见下面的 [表结构详解])

  1. qrtz_job_details:记录每个任务的详细信息。
  2. qrtz_triggers:记录每个触发器的详细信息。
  3. qrtz_corn_triggers:记录 cornTrigger 的信息。
  4. qrtz_simple_triggers:存储 SimpleTrigger 类型触发器的时间信息。
  5. qrtz_simprop_triggers:记录带有简单属性的触发器信息(仅在需要时使用)。
  6. qrtz_blob_triggers:存储复杂触发器(如可能包含序列化对象的触发器)。
  7. qrtz_calendars:记录调度器使用的日历信息(用于排除任务触发时间,如节假日)。
  8. qrtz_fired_triggers:记录每个正在执行的触发器。
  9. qrtz_paused_trigger_grps:记录被暂停的触发器分组。
  10. qrtz_scheduler_state:记录 调度器(每个机器节点)的生命状态。
  11. qrtz_locks:记录程序的悲观锁(防止多个节点同时执行同一个定时任务)。

我们会发现,每个表都有一个 SCHED_NAME 字段,Quartz Scheduler 名字。

这样就实现了每个 Quartz 集群,在数据层面的拆分。

数据源配置类

@Configuration
public class DataSourceConfiguration {/*** 创建 user 数据源的配置对象*/@Primary@Bean(name = "userDataSourceProperties")@ConfigurationProperties(prefix = "spring.datasource.user") // 读取 spring.datasource.user 配置到 DataSourceProperties 对象public DataSourceProperties userDataSourceProperties() {return new DataSourceProperties();}/*** 创建 user 数据源*/@Primary@Bean(name = "userDataSource")@ConfigurationProperties(prefix = "spring.datasource.user.hikari")// 读取 spring.datasource.user 配置到 HikariDataSource 对象public DataSource userDataSource() {// 获得 DataSourceProperties 对象DataSourceProperties properties = this.userDataSourceProperties();// 创建 HikariDataSource 对象return createHikariDataSource(properties);}/*** 创建 quartz 数据源的配置对象*/@Bean(name = "quartzDataSourceProperties")@ConfigurationProperties(prefix = "spring.datasource.quartz")// 读取 spring.datasource.quartz 配置到 DataSourceProperties 对象public DataSourceProperties quartzDataSourceProperties() {return new DataSourceProperties();}/*** 创建 quartz 数据源*/@Bean(name = "quartzDataSource")@ConfigurationProperties(prefix = "spring.datasource.quartz.hikari")@QuartzDataSourcepublic DataSource quartzDataSource() {// 获得 DataSourceProperties 对象DataSourceProperties properties = this.quartzDataSourceProperties();// 创建 HikariDataSource 对象return createHikariDataSource(properties);}private static HikariDataSource createHikariDataSource(DataSourceProperties properties) {// 创建 HikariDataSource 对象HikariDataSource dataSource = properties.initializeDataSourceBuilder().type(HikariDataSource.class).build();// 设置线程池名if (StringUtils.hasText(properties.getName())) {dataSource.setPoolName(properties.getName());}return dataSource;}}

创建任务类

@DisallowConcurrentExecution // 保证相同 JobDetail 在多个 JVM 进程中,有且仅有一个节点在执行
public class DemoJob01 extends QuartzJobBean {private final Logger logger = LoggerFactory.getLogger(getClass());@Resourceprivate DemoService demoService;@Overrideprotected void executeInternal(JobExecutionContext context) throws JobExecutionException {logger.info("[executeInternal][我开始执行了, demoService 为 ({})]", demoService);}}public class DemoJob02 extends QuartzJobBean {private final Logger logger = LoggerFactory.getLogger(getClass());@Overrideprotected void executeInternal(JobExecutionContext context) throws JobExecutionException {logger.info("[DemoJob02-executeInternal][我开始执行了]");}}

注意事项

  1. @DisallowConcurrentExecution 注解 不是以 Quartz Job 为维度,保证在多个 JVM 进程中,有且仅有一个节点在执行,而是以 JobDetail 为维度
    • 虽然说,绝大多数情况下,我们会保证一个 Job 和 JobDetail 是一一对应
    • 所以,搞不清楚这个概念的胖友,最好搞清楚这个概念。实在有点懵逼,保证一个 Job 和 JobDetail 是一一对应就对了。
  2. JobDetail 的唯一标识是 JobKey ,使用 name + group 两个属性。
    • 一般情况下,我们只需要设置 name 即可,而 Quartz 会默认 group = DEFAULT
    • 在 Quartz 中,相同 Scheduler 名字的节点,形成一个 Quartz 集群。
    • 我们可以通过 spring.quartz.scheduler-name 配置项,设置 Scheduler 的名字。
  3. 通过在 Job 实现类上添加 @DisallowConcurrentExecution 注解,实现在相同的 Quartz Scheduler 集群中,相同 JobKey 的 JobDetail,保证在多个 JVM 进程中,有且仅有一个节点在执行

定时任务配置

Bean 自动设置
/*** Bean 自动设置** @author chenmeng*/
@Configuration
public class ScheduleConfiguration {public static class DemoJob01Configuration {/*** 创建 DemoJob01 的 JobDetail Bean 对象** @return JobDetail*/@Beanpublic JobDetail demoJob01() {return JobBuilder.newJob(DemoJob01.class).withIdentity("demoJob01") // 名字为 demoJob01.storeDurably() // 没有 Trigger 关联的时候任务是否被保留。因为创建 JobDetail 时,还没 Trigger 指向它,所以需要设置为 true ,表示保留。.build();}/*** 创建 DemoJob01 的 Trigger Bean 对象。* 其中,我们使用 SimpleScheduleBuilder 简单的调度计划的构造器,创建了每 5 秒执行一次,无限重复的调度计划** @return Trigger*/@Beanpublic Trigger demoJob01Trigger() {// 简单的调度计划的构造器SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5) // 频率 - 每 5 秒。.repeatForever(); // 次数 - 无限重复。// Trigger 构造器return TriggerBuilder.newTrigger().forJob(demoJob01()) // 对应 Job 为 demoJob01.withIdentity("demoJob01Trigger") // 名字为 demoJob01Trigger.withSchedule(scheduleBuilder) // 对应 Schedule 为 scheduleBuilder.build();}}public static class DemoJob02Configuration {/*** 创建 DemoJob02 的 JobDetail Bean 对象** @return JobDetail*/@Beanpublic JobDetail demoJob02() {return JobBuilder.newJob(DemoJob02.class).withIdentity("demoJob02") // 名字为 demoJob02.storeDurably() // 没有 Trigger 关联的时候任务是否被保留。因为创建 JobDetail 时,还没 Trigger 指向它,所以需要设置为 true ,表示保留。.build();}/*** 创建 DemoJob02 的 Trigger Bean 对象。* 其中,我们使用 CronScheduleBuilder 基于 Quartz Cron 表达式的调度计划的构造器,* 创建了每第 10 秒执行一次的调度计划。** @return Trigger*/@Beanpublic Trigger demoJob02Trigger() {//  基于 Quartz Cron 表达式的调度计划的构造器CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("0/10 * * * * ? *");// Trigger 构造器return TriggerBuilder.newTrigger().forJob(demoJob02()) // 对应 Job 为 demoJob02.withIdentity("demoJob02Trigger") // 名字为 demoJob02Trigger.withSchedule(scheduleBuilder) // 对应 Schedule 为 scheduleBuilder.build();}}}
Schedule 手动设置
/*** Scheduler 手动设置* 执行之前,需要确保项目中只有一个 @SpringBootApplication 注解 或者 指定扫描范围** @author chenmeng*/
@SpringBootTest(classes = QuartzJdbcApplication.class)
public class QuartzSchedulerTest {@Autowiredprivate Scheduler scheduler;@Testpublic void addDemoJob01Config() throws SchedulerException {// 创建 JobDetailJobDetail jobDetail = JobBuilder.newJob(DemoJob01.class).withIdentity("demoJob01") // 名字为 demoJob01.storeDurably() // 没有 Trigger 关联的时候任务是否被保留。因为创建 JobDetail 时,还没 Trigger 指向它,所以需要设置为 true ,表示保留。.build();// 创建 TriggerSimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5) // 频率。.repeatForever(); // 次数。Trigger trigger = TriggerBuilder.newTrigger().forJob(jobDetail) // 对应 Job 为 demoJob01.withIdentity("demoJob01Trigger") // 名字为 demoJob01Trigger.withSchedule(scheduleBuilder) // 对应 Schedule 为 scheduleBuilder.build();// 添加调度任务(将 JobDetail 和 Trigger 持久化到数据库)scheduler.scheduleJob(jobDetail, trigger);}@Testpublic void addDemoJob02Config() throws SchedulerException {// 创建 JobDetailJobDetail jobDetail = JobBuilder.newJob(DemoJob02.class).withIdentity("demoJob02") // 名字为 demoJob02.storeDurably() // 没有 Trigger 关联的时候任务是否被保留。因为创建 JobDetail 时,还没 Trigger 指向它,所以需要设置为 true ,表示保留。.build();// 创建 TriggerCronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("0/10 * * * * ? *");Trigger trigger = TriggerBuilder.newTrigger().forJob(jobDetail) // 对应 Job 为 demoJob01.withIdentity("demoJob02Trigger") // 名字为 demoJob01Trigger.withSchedule(scheduleBuilder) // 对应 Schedule 为 scheduleBuilder.build();// 添加调度任务(将 JobDetail 和 Trigger 持久化到数据库)scheduler.scheduleJob(jobDetail, trigger);// 如果想要覆盖数据库中的 Quartz 定时任务的配置,可以调用此方法,传入 replace = true 进行覆盖配置。// scheduler.scheduleJob(jobDetail, Sets.newSet(trigger), true);}}

启动测试

第一个服务:

@SpringBootApplication
public class QuartzJdbcApplication {public static void main(String[] args) {SpringApplication.run(QuartzJdbcApplication.class, args);}}

第二个服务:

/*** 测试集群下的运行情况** @author chenmeng*/
@SpringBootApplication
public class QuartzJdbcApplication02 {public static void main(String[] args) {// 设置 Tomcat 随机端口System.setProperty("server.port", "0");// 启动 Spring Boot 应用SpringApplication.run(QuartzJdbcApplication02.class, args);}}

Bug 记录

异常信息:

# 找不到数据源
Failed to obtain DB connection from data source 'quartzDataSource': java.sql.SQLException: There is no DataSource named 'quartzDataSource'

解决方案:

配置文件:

  • 移除或注释掉 class: org.quartz.impl.jdbcjobstore.JobStoreTX 配置项

原因分析

为什么注释掉 class: org.quartz.impl.jdbcjobstore.JobStoreTX 能解决问题?

在 Spring Boot 2.6.0 及以上版本中,Quartz 的配置机制进行了增强,尤其是对于 JobStore 的自动配置:

  1. Spring Boot 自动管理 JobStore 类型
    • Spring Boot 会根据 spring.quartz.job-store-type 属性自动推断 JobStore的实现类:
      • memory:使用内存存储(RAMJobStore)。
      • jdbc:使用数据库存储(JobStoreTX)。
    • 如果你显式指定了 org.quartz.jobStore.class,则会覆盖 Spring Boot 的默认行为,可能导致配置不兼容或冲突。
  2. Spring Boot Quartz Starter 的改进
    • Spring Boot 的 spring-boot-starter-quartz 在 2.6.0+ 中已经内置了对 JobStoreTX 的支持,
    • 只需要正确配置 job-store-type: jdbc,无需手动设置 org.quartz.jobStore.class
  3. 手动配置 class: org.quartz.impl.jdbcjobstore.JobStoreTX 会冲突
    • 如果手动配置了 org.quartz.jobStore.class,Spring Boot 的自动配置逻辑可能会重复设置 JobStore,从而引发问题,例如无法正确初始化数据源或 JobStore

表结构详解

以下是 Quartz 2.3.0 官方脚本中 11 张表的说明及其功能描述:

1、qrtz_job_detailsQRTZ_JOB_DETAILS

  • 功能:记录每个定时任务的详细信息。
  • 主要字段:
    • JOB_NAMEJOB_GROUP:任务名称和任务分组,联合主键。
    • DESCRIPTION:任务的描述信息。
    • JOB_CLASS_NAME:执行任务的类的全限定名。
    • IS_DURABLE:是否是持久任务(即任务是否在没有触发器时保留)。
    • JOB_DATA:任务附加的键值对数据。

2、qrtz_triggersQRTZ_TRIGGERS

  • 功能:记录每个触发器的详细信息。
  • 主要字段:
    • TRIGGER_NAMETRIGGER_GROUP:触发器名称和分组,联合主键。
    • JOB_NAMEJOB_GROUP:关联的任务名称和分组。
    • TRIGGER_STATE:触发器当前的状态(如等待、暂停、阻塞等)。
    • TRIGGER_TYPE:触发器的类型(如 Cron、Simple、Blob 等)。
    • START_TIMEEND_TIME:触发器的起始时间和结束时间。
    • MISFIRE_INSTR:触发器的误操作策略。

3、qrtz_corn_triggersQRTZ_CRON_TRIGGERS

  • 功能:存储 CronTrigger 类型触发器的时间表达式。
  • 主要字段:
    • TRIGGER_NAMETRIGGER_GROUP:触发器名称和分组(外键关联 QRTZ_TRIGGERS 表)。
    • CRON_EXPRESSION:Cron 表达式。
    • TIME_ZONE_ID:时区 ID。

4、qrtz_simple_triggersQRTZ_SIMPLE_TRIGGERS

  • 功能:存储 SimpleTrigger 类型触发器的时间信息。
  • 主要字段:
    • TRIGGER_NAMETRIGGER_GROUP:触发器名称和分组(外键关联 QRTZ_TRIGGERS 表)。
    • REPEAT_COUNT:触发次数。
    • REPEAT_INTERVAL:触发间隔。
    • TIMES_TRIGGERED:已触发次数。

5、qrtz_simprop_triggersQRTZ_SIMPROP_TRIGGERS

  • 功能:记录带有简单属性的触发器信息(仅在需要时使用)。
  • 主要字段:
    • TRIGGER_NAMETRIGGER_GROUP:触发器名称和分组(外键关联 QRTZ_TRIGGERS 表)。
    • STR_PROP_1 ~ STR_PROP_4:自定义字符串属性。
    • INT_PROP_1 ~ INT_PROP_2:自定义整数属性。
    • DEC_PROP_1 ~ DEC_PROP_2:自定义小数属性。
    • BOOL_PROP_1:自定义布尔属性。

6、qrtz_blob_triggersQRTZ_BLOB_TRIGGERS

  • 功能:存储复杂触发器(如可能包含序列化对象的触发器)。
  • 主要字段:
    • TRIGGER_NAMETRIGGER_GROUP:触发器名称和分组(外键关联 QRTZ_TRIGGERS 表)。
    • BLOB_DATA:二进制大对象数据。

7、qrtz_calendarsQRTZ_CALENDARS

  • 功能:记录调度器使用的日历信息(用于排除任务触发时间,如节假日)。
  • 主要字段:
    • CALENDAR_NAME:日历名称。
    • CALENDAR:序列化后的日历数据。

8、qrtz_fired_triggersQRTZ_FIRED_TRIGGERS

  • 功能:记录每个正在执行的触发器实例。
  • 主要字段:
    • ENTRY_ID:执行条目的唯一标识。
    • TRIGGER_NAMETRIGGER_GROUP:触发器名称和分组。
    • INSTANCE_NAME:触发器所属的调度器实例。
    • FIRED_TIME:触发时间。
    • PRIORITY:触发器优先级。

9、qrtz_paused_trigger_grpsQRTZ_PAUSED_TRIGGER_GRPS

  • 功能:记录被暂停的触发器分组。
  • 主要字段:
    • TRIGGER_GROUP:被暂停的触发器分组名称。

10、qrtz_scheduler_stateQRTZ_SCHEDULER_STATE

  • 功能:记录调度器(每个机器节点)的运行状态。
  • 主要字段:
    • INSTANCE_NAME:调度器实例的名称。
    • LAST_CHECKIN_TIME:调度器最后一次心跳的时间。
    • CHECKIN_INTERVAL:心跳间隔。

11、qrtz_locksQRTZ_LOCKS

  • 功能:记录调度器的全局锁(用于悲观锁,防止多节点竞争执行同一个任务)。
  • 主要字段:
    • SCHED_NAME:调度器名称。
    • LOCK_NAME:锁的名称。

表之间的关系

  • QRTZ_JOB_DETAILS 是核心任务表,记录任务基本信息。
  • QRTZ_TRIGGERS 与任务相关联,存储触发器的基本信息。
  • QRTZ_CRON_TRIGGERSQRTZ_SIMPLE_TRIGGERSQRTZ_BLOB_TRIGGERS 等扩展触发器表,存储不同类型触发器的详细信息。
  • QRTZ_SCHEDULER_STATEQRTZ_LOCKS 用于调度器的分布式协调。

这 11 张表共同支撑了 Quartz 的任务调度功能,既支持单机调度,也支持分布式集群环境。

学习参考

  • Quartz 入门详解
  • 芋道 Spring Boot 定时任务入门
  • Quartz框架(二)——jobstore数据库表字段详解 - 简书
  • springboot升级2.6.x,Quartz2.3.2找不到数据源_there is no datasource named 'quartzdatasource-CSDN博客

相关文章:

【任务调度】Quartz入门

Quartz 入门 代码仓库地址&#xff1a; GitHub&#xff1a;chenmeng-test-demos/demo8-task at master cmty256/chenmeng-test-demosGitee&#xff1a;demo8-task chenmeng/chenmeng-test-demos - 码云 - 开源中国 基本介绍 Quartz 是一个开源的作业调度框架&#xff0c;它完…...

【网络编程】从零开始彻底了解网络编程(二)

本篇博客给大家带来的是网络编程的知识点,. &#x1f40e;文章专栏: JavaEE初阶 &#x1f680;若有问题 评论区见 ❤ 欢迎大家点赞 评论 收藏 分享 如果你不知道分享给谁,那就分享给薯条. 你们的支持是我不断创作的动力 . 王子,公主请阅&#x1f680; 要开心要快乐顺便进步 1. …...

常见浏览器 WebDriver 驱动下载

以下是常见浏览器 WebDriver 驱动的下载地址及注意事项&#xff0c;综合多个可靠来源整理而成&#xff1a; 一、Chrome 浏览器&#xff08;ChromeDriver&#xff09; 官方下载地址 http://chromedriver.storage.googleapis.com/index.html • • 版本匹配&#xff1a;需与 Chro…...

【每日八股】复习计算机网络 Day3:TCP 协议的其他相关问题

文章目录 昨日内容复习TCP 的四次挥手&#xff1f;TCP 为什么要四次挥手&#xff1f;在客户端处于 FIN_WAIT_2 状态时&#xff0c;如果此时收到了乱序的来自服务端的 FIN 报文&#xff0c;客户端会如何处理&#xff1f;何时进入 TIME_WAIT 状态&#xff1f;TCP 四次挥手丢了怎么…...

大模型在胆管结石(无胆管炎或胆囊炎)预测及治疗方案制定中的应用研究

目录 一、引言 1.1 研究背景与意义 1.2 研究目的 1.3 国内外研究现状 二、胆管结石相关理论基础 2.1 胆管结石概述 2.2 临床表现与诊断方法 2.3 传统治疗方法 三、大模型技术原理与应用优势 3.1 大模型基本原理 3.2 在医疗领域的应用潜力 3.3 用于胆管结石预测的可…...

LeetCode第159题_至多包含两个不同字符的最长子串

LeetCode 第159题&#xff1a;至多包含两个不同字符的最长子串 题目描述 给定一个字符串 s&#xff0c;找出 至多 包含两个不同字符的最长子串 t&#xff0c;并返回该子串的长度。 难度 中等 题目链接 点击在LeetCode中查看题目 示例 示例 1&#xff1a; 输入: s &qu…...

PG CTE 递归 SQL 翻译为 达梦版本

文章目录 PG SQLDM SQL总结 PG SQL with recursive result as (select res_id,phy_res_code,res_name from tbl_res where parent_res_id (select res_id from tbl_res where phy_res_code org96000#20211203155858) and res_type_id 1 union all select t1.res_id, t1.p…...

JavaScript 位掩码常量教程

JavaScript 位掩码常量教程 位掩码&#xff08;Bitmask&#xff09;是一种高效使用内存的技术&#xff0c;在JavaScript中可以用来存储和操作多个布尔值标志。下面我将为您介绍位掩码的基本概念、应用场景以及实践示例。 什么是位掩码常量&#xff1f; 位掩码利用二进制位&a…...

Linux守护进程

一、相关概念 QQ邮箱关于三种协议的解释&#xff1a;SMTP/IMAP服务 1.SMTP协议 SMTP&#xff08;​​Simple Mail Transfer Protocol​​&#xff0c;简单邮件传输协议&#xff09;是一种用于发送电子邮件的互联网标准。它在TCP/IP协议族中&#xff0c;通常使用25端口进行通…...

Python多进程并发编程:深入理解Lock与Semaphore的实战应用与避坑指南

引言 在多进程并发编程中&#xff0c;资源竞争问题如同“隐形炸弹”&#xff0c;稍有不慎就会导致数据不一致或程序崩溃。无论是银行转账的余额错误&#xff0c;还是火车票超卖&#xff0c;其根源都在于共享资源的无序访问。如何安全高效地管理这些资源&#xff1f;Python中的锁…...

mysql的5.7版本与8.0版本的差异与兼容性

MySQL 5.7 和 8.0 是两个重要的版本&#xff0c;它们在性能、功能、安全性等方面都有显著的改进&#xff0c;同时也存在一些兼容性问题。以下是具体的改进点和兼容性问题&#xff1a; 一、MySQL 8.0 的改进点 性能提升 优化器改进&#xff1a;MySQL 8.0 对查询优化器进行了重大…...

【Rust 精进之路之第4篇-数据基石·上】标量类型:整数、浮点数、布尔与字符的精妙之处

系列: Rust 精进之路:构建可靠、高效软件的底层逻辑 作者: 码觉客 发布日期: 2025-04-20 引言:构成万物的“原子”——标量类型 在上一篇文章【变量观】中,我们深入探讨了 Rust 如何通过 let、mut、const、static 和 Shadowing 来管理变量绑定,并理解了其背后对安全性…...

LangChain4j模型参数配置全解析:释放大语言模型的真正潜力

LangChain4j模型参数配置全解析&#xff1a;释放大语言模型的真正潜力 前言 在大语言模型应用开发中&#xff0c;参数配置是连接算法理论与工程实践的关键桥梁。合理的参数设置能让模型输出更精准、响应更高效&#xff0c;而错误的配置可能导致成本激增或业务逻辑失效。本文将…...

【深度学习入门_NLP自然语言处理】序章

本部分开始深度学习第二大部分NLP章节学习&#xff0c;找了好多资料&#xff0c;终于明确NLP的学习目标了&#xff0c;介于工作之余学习综合考量&#xff0c;还是决定以视频学习为主后期自主实践为主吧。 分享一个总图&#xff0c;其实在定位的时候很迷茫&#xff0c;单各章节…...

计算机组成原理笔记(十六)——4.1基本算术运算的实现

计算机中最基本的算术运算是加法运算&#xff0c;加、减、乘、除运算最终都可以归结为加法运算。 4.1.1加法器 一、加法器的基本单元 加法器的核心单元是 全加器&#xff08;Full Adder, FA&#xff09;&#xff0c;而所有加法器都由 半加器&#xff08;Half Adder, HA&…...

AI日报 - 2025年04月21日

&#x1f31f; 今日概览(60秒速览) ▎&#x1f916; AGI突破 | O3模型性能引热议&#xff0c;Rich Sutton提出「体验时代」新范式&#xff0c;自递归AI构建仍存挑战。 新模型如O3展示高IQ&#xff0c;但AGI定义与实现路径讨论加剧&#xff0c;强调自主生成数据与体验学习。 ▎&…...

基于Python的推荐算法的电影推荐系统的设计

标题:基于Python的推荐算法的电影推荐系统的设计与实现 内容:1.摘要 本文围绕基于Python的推荐算法的电影推荐系统展开研究。背景在于随着电影数量的急剧增加&#xff0c;用户在海量电影中找到符合自身喜好的影片变得困难。目的是设计并实现一个高效准确的电影推荐系统&#x…...

【perf】perf工具的使用生成火焰图

文章目录 1. What is perf?2. perf使用2.1 perf的子工具集2.2 常用指令perf list指令格式参数perf中事件分类使用示例 perf stat指令格式参数 perf top指令格式参数交互式界面操作使用示例 perf record指令格式参数使用示例 perf report指令格式参数交互式界面操作使用示例 pe…...

Sentinel源码—6.熔断降级和数据统计的实现一

大纲 1.DegradeSlot实现熔断降级的原理与源码 2.Sentinel数据指标统计的滑动窗口算法 1.DegradeSlot实现熔断降级的原理与源码 (1)熔断降级规则DegradeRule的配置Demo (2)注册熔断降级监听器和加载熔断降级规则 (3)DegradeSlot根据熔断降级规则对请求进行验证 (1)熔断降级…...

C语言自增自减题目

一、题目引入 二、运行结果 三、题目分析 这一题中 i的初始值是2 所以执行case2中的命令i-- 表达式的值此时是2 i--完了之后i最后的值是1 由于是switch没有break 就会往下贯穿 直到遇到break为止 case3里面 i 表达式的值是2 i完了之后i的值也是2 综上所述 i的值最终的值是…...

paddleocr出现: [WinError 127] 找不到指定的程序解决办法

paddleocr是一个由百度开发开源的OCR&#xff08;光学字符识别&#xff09;工具库。它支持多种语言的文本识别&#xff0c;包括中文、英文、日文等&#xff0c;并具备高效的文本检测和识别能力。paddleocr基于PaddlePaddle深度学习框架开发&#xff0c;提供了丰富的预处理、模型…...

c++STL——list的使用和模拟实现

文章目录 list的使用和模拟实现使用部分list的结构声名默认成员函数initializer_list容量和访问操作修改操作其他接口list的迭代器迭代器的种类 list的模拟实现明确基本结构预处理函数迭代器部分(重点)思路进一步考虑最终代码operator->的重载总结 begin和end访问接口修改操…...

交换机端口安全

端口安全 端口安全(PortSecurity)通过将接口学习到的动态MAC地址转换为安全MAC地址&#xff08;包括安全动态MAC、安全静态MAC和Sticky MAC)&#xff0c;阻止非法用户通过本接口和交换机通信&#xff0c;从而增强设备的安全性。 1、安全mac地址分类 安全动态MAC地址&#xf…...

【Oracle专栏】Oracle中的虚拟列

Oracle相关文档&#xff0c;希望互相学习&#xff0c;共同进步 风123456789&#xff5e;-CSDN博客 1.背景 在EXP方式导出时&#xff0c;发现 出现如下提示 EXP-00107: virtual column 不支持&#xff0c;因此采用expdp方式导出。于是本文针对oracle虚拟列进行简单介绍。 2. 相…...

shell 正则表达式与文本处理器

目录 前言 一、正则表达式 &#xff08;一&#xff09;定义与用途 &#xff08;二&#xff09;基础正则表达式 &#xff08;三&#xff09;基础正则表达式元字符 &#xff08;四&#xff09;扩展正则表达式 二、文本处理器&#xff1a;Shell 编程的得力助手 &#xff0…...

ZYNQ笔记(九):定时器中断

版本&#xff1a;Vivado2020.2&#xff08;Vitis&#xff09; 任务&#xff1a;使用定时器 (私有定时器) 中断 实现 LED&#xff08;PS端&#xff09; 定时1s亮灭翻转 目录 一、介绍 二、硬件设计 三、软件设计 四、效果 一、介绍 Zynq系列是Xilinx(现为AMD)推出的集成了AR…...

idea中运行groovy程序报错

我的项目是使用的 gradle 构建的。 在 idea 中运行Groovy的面向对象程序报错如下&#xff1a; Execution failed for task :Person.main(). > Process command G:/Program Files/jdk-17/jdk-17.0.12/bin/java.exe finished with non-zero exit value 1* Try: Run with --s…...

具身智能零碎知识点(四):联合嵌入预测架构(JEPAs)详解

联合嵌入预测架构&#xff08;JEPAs&#xff09;详解 联合嵌入预测架构&#xff08;JEPAs&#xff09;详解一、核心思想二、技术原理1. 核心组件2. 训练目标 三、与传统方法的对比四、具体实例例1&#xff1a;视频预测&#xff08;如Meta的I-JEPA&#xff09;例2&#xff1a;多…...

linux 搭建 dvwa 渗透测试环境

linux 安装 dvwa 1、分为4个部分&#xff0c;搭建dvwa渗透测试环境2、安装centos 7.63、安装apache http server4、安装mysql5、安装php6、运行dvwa 1、分为4个部分&#xff0c;搭建dvwa渗透测试环境 本文基于centos 7.6 搭建 dvwa 渗透测试环境 安装一个linux系统安装apache…...

C++项目 —— 基于多设计模式下的同步异步日志系统(4)(双缓冲区异步任务处理器(AsyncLooper)设计)

C项目 —— 基于多设计模式下的同步&异步日志系统&#xff08;4&#xff09;&#xff08;双缓冲区异步任务处理器&#xff08;AsyncLooper&#xff09;设计&#xff09; 异步线程什么是异步线程&#xff1f;C 异步线程简单例子代码解释程序输出关键点总结扩展&#xff1a;使…...

【Linux学习笔记】Linux的环境变量和命令行参数

【Linux学习笔记】Linux的环境变量和命令行参数 &#x1f525;个人主页&#xff1a;大白的编程日记 &#x1f525;专栏&#xff1a;Linux学习笔记 文章目录 【Linux学习笔记】Linux的环境变量和命令行参数前言一.环境变量1.1基本概念1.2常见环境变量1.3和环境变量相关的命令1…...

排序算法-快速排序

描述&#xff1a; 基准值选择&#xff1a;选取数组的最后一个元素 arr[high] 作为基准值 p。初始化索引&#xff1a;i 初始化为 low - 1&#xff0c;其作用是指向比基准值小的最后一个元素的索引。遍历数组&#xff1a;借助 for 循环从 low 到 high - 1 遍历数组。若当前元素 …...

软考高级系统架构设计师-第16章 数学与经济管理

【本章学习建议】 根据考试大纲&#xff0c;本章主要考查系统架构设计师单选题&#xff0c;预计考2分左右。主要是运筹学的计算问题&#xff0c;范围广、难度大&#xff0c;超纲题较多&#xff0c;不用深究。 16.1 线性规划 线性规划是研究在有限的资源条件下&#xff0c;如果…...

爱在冰川-慢就是快

【游资大佬の搞钱心法&#x1f525;&#xff5c;小白逆袭必看冰川语录真实案例‼️】 &#x1f4a1;刚扒完爱在冰川的万字访谈 发现游资搞钱真的靠"反人性思维" 总结6条狠人法则真实案例 建议收藏反复背诵&#x1f447; 1️⃣【周期为王】&#x1f4ab; "行情…...

Mac-VScode-C++环境配置

mac上自带了clang所以不是必须下载Homebrew 下面是配置文件&#xff08;注释记得删一下&#xff09; package.json {"name": "git-base","displayName": "%displayName%","description": "%description%",&quo…...

【JAVA EE初阶】多线程(1)

这样的代码&#xff0c;虽然也能打印hello thread&#xff0c;但是没有创建新的线程&#xff0c;而是直接在main方法所在的主线程中执行了run的逻辑 start方法&#xff0c;是调用系统api&#xff0c;真正在操作系统内部创建一个线程。这个新的线程会以run作为入口方法&#xff…...

PHP伪协议读取文件

借鉴php伪协议实现命令执行,任意文件读取_ctf php文件读取-CSDN博客 总结 在ctf中常用的有data:// , php://input , php://filter &#xff0c;file:// php://input &#xff0c;data://用来执行命令 1.php://input 的用法 http://127.0.0.1/include.php?filephp://input [P…...

动态调整映射关系的一致性哈希负载均衡算法详解

一、核心原理与设计要点 双重映射结构 一致性哈希负载均衡通过 哈希环 和 槽动态分配 实现双重映射关系&#xff1a; • 哈希环构建&#xff1a;将节点&#xff08;物理或虚拟&#xff09;和数据键&#xff08;Key&#xff09;通过哈希函数&#xff08;如MD5、CRC32&#xff09…...

控制反转(IOC)和依赖注入(DI)

Target Retention Documented 元注解 Component 将类交给IOC容器管理&#xff0c;成为IOC容器中的bean Autowired 注入运行时所需要依赖的对象 因为Mabatis DAO层注解Reponsitory 基本不用了&#xff0c;现在Mapper层Mapper注解&#xff0c;这里的Mapper层相当于原来的DAO层…...

【每日八股】复习 MySQL Day1:事务

文章目录 复习 MySQL Day1&#xff1a;事务MySQL 事务的四大特性&#xff1f;并发事务会出现什么问题&#xff1f;MySQL 事务的隔离级别&#xff1f;不同事务隔离级别下会发生什么问题&#xff1f;MVCC 的实现原理&#xff1f;核心数据结构版本链构建示例可见性判断算法MVCC 可…...

【数据结构和算法】1. 数据结构和算法简介、二分搜索

本文根据 数据结构和算法入门 视频记录 文章目录 1. 数据结构和算法简介1.1 什么是数据结构&#xff1f;什么是算法&#xff1f;1.2 数据结构和算法之间的关系1.3 “数据结构和算法”有那么重要吗&#xff1f; 2. 二分搜索&#xff08;Binary Search&#xff09;2.1 算法概念2…...

4月19日记(补)算了和周日一块写了 4月20日日记

周六啊 昨天晚上又玩的太嗨了。睡觉的时候有点晚了&#xff0c;眼睛疼就没写日记。现在补上 实际上现在是20号晚上八点半了。理论上来说应该写今天的日记。 周六上午打比赛啦&#xff0c;和研究生&#xff0c;输了&#xff0c;我是替补没上场。没关系再练一练明天就可以变强…...

面试常用基础算法

目录 快速排序归并排序堆排序 n n n皇后问题最大和子数组爬楼梯中心扩展法求最长回文子序列分割回文串动态规划求最长回文子序列最长回文子串单调栈双指针算法修改 分割回文串滑动窗口栈 快速排序 #include <iostream> #include <algorithm>using namespace std;…...

微服务与 SOA:架构异同全解析与应用指南

微服务和 SOA&#xff08;面向服务的架构&#xff09;是两种不同的软件架构风格&#xff0c;它们在很多方面存在相似之处&#xff0c;但也有一些区别。以下是对它们的详细介绍&#xff1a; 一、概念 1.微服务 微服务架构将一个大型应用程序拆分成多个小型、独立的服务&#…...

Dijkstra 算法入门笔记 (适用于算法竞赛初学者) - C++ 代码版

目录 算法是做什么的&#xff1f;核心思想&#xff1a;贪就完事了&#xff01;算法前提&#xff1a;不能有负权边&#xff01;需要哪些工具&#xff1f;(数据结构)算法具体步骤关键操作&#xff1a;松弛 (Relaxation)两种实现方式 (C 代码) 朴素版 Dijkstra (O(V^2))堆优化版 …...

脑影像分析软件推荐| GraphVar介绍

目录 1.软件界面 2.工具包功能简介 3.软件安装注意事项 1.软件界面 2.工具包功能简介 GraphVar是一个用户友好的 MATLAB 工具箱&#xff0c;用于对功能性大脑连接进行全面的图形分析。这里我们介绍了该工具箱的全面扩展&#xff0c;使用户能够无缝探索跨功能连接测量的可轻…...

如何优雅地实现全局唯一?深入理解单例模式

如何优雅地实现全局唯一&#xff1f;深入理解单例模式 一、什么是单例模式&#xff1f; 单例模式是一种创建型设计模式&#xff0c;旨在确保一个类只有一个实例&#xff0c;并为该实例提供全局访问点&#xff0c;从而避免全局变量的命名污染&#xff0c;并支持延迟初始化Wiki…...

【Flutter】使用LiveKit和Flutter构建实时视频聊天应用

引言 在当今快速发展的数字世界中&#xff0c;实时视频通信已成为许多应用程序的核心功能。无论是远程工作、在线教育还是社交网络&#xff0c;高质量的实时视频功能都至关重要。LiveKit作为一个开源的WebRTC解决方案&#xff0c;提供了构建可扩展实时音视频应用所需的一切工具…...

Android Jetpack Compose 状态管理解析:remember vs mutableStateOf,有啥不一样?为啥要一起用?

&#x1f331;《Jetpack Compose 状态管理解析&#xff1a;remember vs mutableStateOf&#xff0c;有啥不一样&#xff1f;为啥要一起用&#xff1f;》 在 Jetpack Compose 的世界里&#xff0c;UI 是响应式的。这意味着当状态发生变化时&#xff0c;UI 会自动重组&#xff0…...

QT6 源(37):界面组件的总基类 QWidget 的源码阅读(下,c++ 代码部分)

&#xff08;1&#xff09; QT 在 c 的基础上增加了自己的编译器&#xff0c;以支持元对象系统和 UI 界面设计&#xff0c;有 MOC 、 UIC 等 QT 自己的编译器。本节的源代码里&#xff0c;为了减少篇幅&#xff0c;易于阅读&#xff0c;去除了上篇中的属性部分&#xff0c; 上篇…...