Quartz 定時任務持久化(重啓後自動恢復)
聲明: 本文內容由 ChatGPT 協助生成,僅作為個人學習與記錄之用。
Quartz 默認使用 RAMJobStore(內存存儲),服務重啓後任務會丟失。
要讓定時任務在重啓後仍然有效,必須啓用:JDBCJobStore(數據庫持久化)
本文説明如何在 Spring Boot 項目中配置 Quartz 持久化,使任務存入數據庫並在重啓後自動恢復。
1. 啓用 Quartz 持久化(application.yml)
示例配置:
spring:
quartz:
job-store-type: jdbc # 啓用數據庫持久化
jdbc:
initialize-schema: always # 第一次啓動自動建表,之後改為 never
properties:
org.quartz.scheduler.instanceName: QuartzScheduler
org.quartz.scheduler.instanceId: AUTO
org.quartz.jobStore.class: org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
org.quartz.jobStore.useProperties: false
org.quartz.jobStore.tablePrefix: QRTZ_
org.quartz.threadPool.threadCount: 10
注意:
initialize-schema: always只在第一次啓動時用
第二次以後必須改為 never,避免自動重建表導致任務丟失。
- 數據庫需先創建好,Quartz 會自動建表(第一次)。
2. 初始化數據庫(Quartz 表結構)
Quartz 內置表結構 SQL,可在 quartz-x.x.jar 中找到:
路徑:
org/quartz/impl/jdbcjobstore/
根據數據庫選擇:
| 數據庫 | SQL 文件 |
|---|---|
| MySQL | tables_mysql_innodb.sql |
| PostgreSQL | tables_postgres.sql |
| Oracle | tables_oracle.sql |
執行後會生成 11 張表,例如:
-
QRTZ_JOB_DETAILS -
QRTZ_TRIGGERS -
QRTZ_CRON_TRIGGERS -
QRTZ_SIMPLE_TRIGGERS -
QRTZ_FIRED_TRIGGERS -
QRTZ_SCHEDULER_STATE -
QRTZ_LOCKS
...
這些表記錄 Job/Trigger,實現持久化。
3. 可選:為 Job 啓用持久化註解(存儲 JobDataMap)
如果你的 Job 需要持久化任務狀態,添加:
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class MyJob implements Job {
...
}
功能説明:
| 註解 | 作用 |
|---|---|
| @PersistJobDataAfterExecution | 執行後的JobDataMap數據狀態寫回數據庫 |
| @DisallowConcurrentExecution | 任務串行執行,避免讀寫衝突 |
@DisallowConcurrentExecution 的作用
防止同一個 Job 的多個實例併發執行。
也就是説:
Quartz 會等待當前 Job 執行完,才會執行下一次觸發。
為什麼需要這個註解?
Quartz 默認行為是:
-
假設你的 Job 計劃每 5 秒 執行一次
-
但你的任務實際執行時間是 10 秒
那麼:
-
Quartz 會在第 5 秒再併發啓動一個 Job 實例
-
第 10 秒再啓動一個
-
這樣會導致同一個 Job 多實例併發執行
這在很多業務場景是危險的:
-
寫數據庫時造成髒數據
-
寫文件導致衝突
-
調接口重複提交
-
修改共享變量時出併發問題
加上 @DisallowConcurrentExecution 後
Quartz 保證:
✔ 第一個任務沒執行完
✔ Quartz 不會再啓動第二個
✔ 任務之間嚴格串行執行
✔ 安全性強
@PersistJobDataAfterExecution 的作用
讓你在 Job 裏面修改的參數(JobDataMap)能被保存下來,下次執行還能繼續用。
舉個最簡單的例子
你有個定時任務,每次執行想讓計數器 count +1:
int count = data.getInt("count");
data.put("count", count + 1);
如果 沒有 @PersistJobDataAfterExecution:
-
每次執行 count 都從 0 開始
-
因為 Quartz 不會把你更新的值保存下來
如果 加上 @PersistJobDataAfterExecution:
-
count 會變成 1、2、3、4...
-
Quartz 會把更新後的值寫回數據庫
-
服務重啓後 count 也不會丟
只要你在 job 裏對 JobDataMap 做寫操作,想保存結果 → 就一定要加 @PersistJobDataAfterExecution + @DisallowConcurrentExecution。(防止併發造成的數據覆蓋和丟失)
如果你只需要任務被保存,而不需要保存 JobDataMap,可以不加這兩個註解。
4. 創建 Job 時必須設置 .storeDurably()
持久化 Job 的關鍵:
JobDetail jobDetail = JobBuilder.newJob(MyJob.class)
.withIdentity("job1", "group1")
.storeDurably() // ★★★★★ 必須,Job 才會存入數據庫
.build();
不加 storeDurably() 的 Job 會被當成“臨時 Job”,服務重啓後會丟失。
Trigger 默認會持久化,不需要額外配置。
5. 服務重啓後自動恢復機制
Quartz 啓動時會自動從以下表中加載任務:
-
QRTZ_JOB_DETAILS -
QRTZ_TRIGGERS -
QRTZ_CRON_TRIGGERS/QRTZ_SIMPLE_TRIGGERS
無需額外代碼。
6. 如何驗證持久化是否生效
-
創建一個 Job + Trigger
-
啓動服務 → 任務執行正常
-
查看數據庫 QRTZ_ 前綴的表,是否有記錄
-
停止服務
-
再次啓動
-
任務是否自動恢復執行
如能恢復,即持久化成功。
參考文章:
【Quartz】(一)定時框架Quartz的持久化配置: https://blog.csdn.net/Jeffhan_java/article/details/123532049