Quartz數據庫表分析 原 薦

系列文章

Spring整合Quartz分佈式調度

Quartz數據庫表分析

Quartz調度源碼分析

前言

上一篇文章Spring整合Quartz分佈式調度介紹了Quartz通過數據庫的方式來實現分佈式調度,通過使用數據庫來存儲trigger,job等信息,可以在停服重啓的時候重新加載上次trigger的狀態,保證了完整性;另一方面通過數據庫來實現鎖機制來實現分佈式調度;Quartz默認提供了11張表,本文將對這幾張表做簡要的分析。

表信息

1.qrtz_blob_triggers
2.qrtz_cron_triggers
3.qrtz_simple_triggers
4.qrtz_simprop_triggers
5.qrtz_fired_triggers
6.qrtz_triggers
7.qrtz_job_details
8.qrtz_calendars
9.qrtz_paused_trigger_grps
10.qrtz_scheduler_state
11.qrtz_locks

共11張表,前6張都是關於各種triggers的信息,後面包括job,悲觀鎖,調度狀態等信息;相關表操作在類StdJDBCDelegate中,相關sql語句在StdJDBCConstants中;

1.qrtz_blob_triggers

自定義的triggers使用blog類型進行存儲,非自定義的triggers不會存放在此表中,Quartz提供的triggers包括:CronTrigger,CalendarIntervalTrigger,
DailyTimeIntervalTrigger以及SimpleTrigger,這幾個trigger信息會保存在後面的幾張表中;

2.qrtz_cron_triggers

存儲CronTrigger,這也是我們使用最多的觸發器,在配置文件中做如下配置,即可在qrtz_cron_triggers生成記錄:

<bean id="firstCronTrigger"
    class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
    <property name="jobDetail" ref="firstTask" />
    <property name="cronExpression" value="0/6 * * ? * *" />
    <property name="group" value="firstCronGroup"></property>
</bean>
<bean id="firstTask"
    class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
    <property name="jobClass" value="zh.maven.SQuartz.task.FirstTask" />
    <property name="jobDataMap">
        <map>
            <entry key="firstService" value-ref="firstService" />
        </map>
    </property>
</bean>
<bean id="firstService" class="zh.maven.SQuartz.service.FirstService"></bean>

表達式指定了每隔6秒執行一次,然後指定了要執行的task,task指定了要執行的業務,運行之後可以查看數據表:

mysql> select * from qrtz_cron_triggers;
+-------------+------------------+----------------+-----------------+---------------+
| SCHED_NAME  | TRIGGER_NAME     | TRIGGER_GROUP  | CRON_EXPRESSION | TIME_ZONE_ID  |
+-------------+------------------+----------------+-----------------+---------------+
| myScheduler | firstCronTrigger | firstCronGroup | 0/6 * * ? * *   | Asia/Shanghai |
+-------------+------------------+----------------+-----------------+---------------+

myScheduler是在定義SchedulerFactoryBean時指定的名稱,其他字段都可以在上面的配置中找到;

3.qrtz_simple_triggers

存儲SimpleTrigger,在配置文件中做如下配置,即可在qrtz_simple_triggers生成記錄:

<bean id="firstSimpleTrigger"
    class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
    <property name="jobDetail" ref="firstSimpleTask" />
    <property name="startDelay" value="1000" />
    <property name="repeatInterval" value="2000" />
    <property name="repeatCount" value="5"></property>
    <property name="group" value="firstSimpleGroup"></property>
</bean>
<bean id="firstSimpleTask"
    class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
    <property name="jobClass" value="zh.maven.SQuartz.task.SimpleFirstTask" />
    <property name="jobDataMap">
        <map>
            <entry key="firstService" value-ref="simpleFirstService" />
        </map>
    </property>
</bean>
<bean id="simpleFirstService" class="zh.maven.SQuartz.service.SimpleFirstService"></bean>

指定了開始延遲時間,重複間隔時間已經重複的次數限制,查看錶如下:

mysql> select * from qrtz_simple_triggers;
+-------------+--------------------+------------------+--------------+-----------------+-----------------+
| SCHED_NAME  | TRIGGER_NAME       | TRIGGER_GROUP    | REPEAT_COUNT | REPEAT_INTERVAL | TIMES_TRIGGERED |
+-------------+--------------------+------------------+--------------+-----------------+-----------------+
| myScheduler | firstSimpleTrigger | firstSimpleGroup |            5 |            2000 |               1 |
+-------------+--------------------+------------------+--------------+-----------------+-----------------+

TIMES_TRIGGERED用來記錄執行了多少次了,此值被定義在SimpleTriggerImpl中,每次執行+1,這裏定義的REPEAT_COUNT=5,實際情況會執行6次,具體可以查看SimpleTriggerImpl源碼:

public Date getFireTimeAfter(Date afterTime) {
        if (complete) {
            return null;
        }
 
        if ((timesTriggered > repeatCount)
                && (repeatCount != REPEAT_INDEFINITELY)) {
            return null;
        }
        ......
}

timesTriggered默認值爲0,當timesTriggered > repeatCount停止trigger,所以會執行6次,當執行完畢之後此記錄會被刪除;

4.qrtz_simprop_triggers

存儲CalendarIntervalTrigger和DailyTimeIntervalTrigger兩種類型的觸發器,使用CalendarIntervalTrigger做如下配置:

<bean id="firstCalendarTrigger" class="org.quartz.impl.triggers.CalendarIntervalTriggerImpl">
    <property name="jobDataMap">
        <map>
            <entry key="jobDetail" value-ref="firstCalendarTask"></entry>
        </map>
    </property>
    <property name="key" ref="calendarTriggerKey"></property>
    <property name="repeatInterval" value="1" />
    <property name="group" value="firstCalendarGroup"></property>
</bean>
<bean id="firstCalendarTask"
    class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
    <property name="jobClass" value="zh.maven.SQuartz.task.CalendarFirstTask" />
    <property name="jobDataMap">
        <map>
            <entry key="firstService" value-ref="calendarFirstService" />
        </map>
    </property>
</bean>
<bean id="calendarFirstService" class="zh.maven.SQuartz.service.CalendarFirstService"></bean>

CalendarIntervalTrigger沒有對應的FactoryBean,直接設置實現類CalendarIntervalTriggerImpl;指定的重複週期是1,默認單位是天,也就是每天執行一次,查看錶如下:

mysql> select * from qrtz_simprop_triggers;
+-------------+--------------------+--------------------+------------+---------------+------------+------------+------------+-------------+-------------+------------+------------+-------------+-------------+
| SCHED_NAME  | TRIGGER_NAME       | TRIGGER_GROUP      | STR_PROP_1 | STR_PROP_2    | STR_PROP_3 | INT_PROP_1 | INT_PROP_2 | LONG_PROP_1 | LONG_PROP_2 | DEC_PROP_1 | DEC_PROP_2 | BOOL_PROP_1 | BOOL_PROP_2 |
+-------------+--------------------+--------------------+------------+---------------+------------+------------+------------+-------------+-------------+------------+------------+-------------+-------------+
| myScheduler | calendarTriggerKey | firstCalendarGroup | DAY        | Asia/Shanghai | NULL       |          1 |          1 |           0 |           0 |       NULL |       NULL | 0           | 0           |
+-------------+--------------------+--------------------+------------+---------------+------------+------------+------------+-------------+-------------+------------+------------+-------------+-------------+

提供了3個string類型的參數,2個int類型的參數,2個long類型的參數,2個decimal類型的參數以及2個boolean類型的參數;具體每個參數是什麼含義,根據不同的trigger類型存放各自的參數;

5.qrtz_fired_triggers

存儲已經觸發的trigger相關信息,trigger隨着時間的推移狀態發生變化,直到最後trigger執行完成,從表中被刪除;已SimpleTrigger爲例重複3次執行,查詢表:

mysql> select * from qrtz_fired_triggers;
+-------------+----------------------------------------+--------------------+------------------+---------------------------+---------------+---------------+----------+-----------+-----------------+-----------+------------------+-------------------+
| SCHED_NAME  | ENTRY_ID                               | TRIGGER_NAME       | TRIGGER_GROUP    | INSTANCE_NAME             | FIRED_TIME    | SCHED_TIME    | PRIORITY | STATE     | JOB_NAME        | JOB_GROUP | IS_NONCONCURRENT | REQUESTS_RECOVERY |
+-------------+----------------------------------------+--------------------+------------------+---------------------------+---------------+---------------+----------+-----------+-----------------+-----------+------------------+-------------------+
| myScheduler | NJD9YZGJ2-PC15241041777351524104177723 | firstSimpleTrigger | firstSimpleGroup | NJD9YZGJ2-PC1524104177735 | 1524104178499 | 1524104178472 |        0 | EXECUTING | firstSimpleTask | DEFAULT   | 0                | 0                 |
| myScheduler | NJD9YZGJ2-PC15241041777351524104177724 | firstSimpleTrigger | firstSimpleGroup | NJD9YZGJ2-PC1524104177735 | 1524104180477 | 1524104180472 |        0 | EXECUTING | firstSimpleTask | DEFAULT   | 0                | 0                 |
| myScheduler | NJD9YZGJ2-PC15241041777351524104177725 | firstSimpleTrigger | firstSimpleGroup | NJD9YZGJ2-PC1524104177735 | 1524104180563 | 1524104182472 |        0 | ACQUIRED  | NULL            | NULL      | 0                | 0                 |
+-------------+----------------------------------------+--------------------+------------------+---------------------------+---------------+---------------+----------+-----------+-----------------+-----------+------------------+-------------------+

相同的trigger和task,每觸發一次都會創建一個實例;從剛被創建的ACQUIRED狀態,到EXECUTING狀態,最後執行完從數據庫中刪除;

6.qrtz_triggers

存儲定義的trigger,以上定義的三個triggers爲例,分別是:firstSimpleTrigger,firstCalendarTrigger和firstCronTrigger,運行之後查看數據庫:

mysql> select * from qrtz_triggers;
+-------------+--------------------+--------------------+-------------------+-----------+-------------+----------------+----------------+----------+---------------+--------------+---------------+----------+---------------+---------------+----------+
| SCHED_NAME  | TRIGGER_NAME       | TRIGGER_GROUP      | JOB_NAME          | JOB_GROUP | DESCRIPTION | NEXT_FIRE_TIME | PREV_FIRE_TIME | PRIORITY | TRIGGER_STATE | TRIGGER_TYPE | START_TIME    | END_TIME | CALENDAR_NAME | MISFIRE_INSTR | JOB_DATA |
+-------------+--------------------+--------------------+-------------------+-----------+-------------+----------------+----------------+----------+---------------+--------------+---------------+----------+---------------+---------------+----------+
| myScheduler | calendarTriggerKey | firstCalendarGroup | firstCalendarTask | DEFAULT   | NULL        |  1524203884719 |  1524117484719 |        5 | WAITING       | CAL_INT      | 1524117484719 |        0 | NULL          |             0 |          |
| myScheduler | firstCronTrigger   | firstCronGroup     | firstTask         | DEFAULT   | NULL        |  1524117492000 |  1524117486000 |        0 | ACQUIRED      | CRON         | 1524117483000 |        0 | firstCalendar |             0 |          |
| myScheduler | firstSimpleTrigger | firstSimpleGroup   | firstSimpleTask   | DEFAULT   | NULL        |             -1 |  1524117488436 |        0 | COMPLETE      | SIMPLE       | 1524117484436 |        0 | NULL          |             0 |          |
+-------------+--------------------+--------------------+-------------------+-----------+-------------+----------------+----------------+----------+---------------+--------------+---------------+----------+---------------+---------------+----------+

和qrtz_fired_triggers存放的不一樣,不管trigger觸發了多少次都只有一條記錄,TRIGGER_STATE用來標識當前trigger的狀態;firstCalendarTask每天執行一次,執行完之後一直是WAITING狀態;firstCronTrigger每6秒執行一次狀態是ACQUIRED狀態;firstSimpleTrigger重複執行6次後狀態爲COMPLETE,並且會被刪除;

7.qrtz_job_details

存儲jobDetails信息,相關信息在定義的時候指定,如上面定義的JobDetailFactoryBean,查詢數據庫:

mysql> select * from qrtz_job_details;
+-------------+-------------------+-----------+-------------+-----------------------------------------+------------+------------------+----------------+-------------------+----------+
| SCHED_NAME  | JOB_NAME          | JOB_GROUP | DESCRIPTION | JOB_CLASS_NAME                          | IS_DURABLE | IS_NONCONCURRENT | IS_UPDATE_DATA | REQUESTS_RECOVERY | JOB_DATA |
+-------------+-------------------+-----------+-------------+-----------------------------------------+------------+------------------+----------------+-------------------+----------+
| myScheduler | firstCalendarTask | DEFAULT   | NULL        | zh.maven.SQuartz.task.CalendarFirstTask | 0          | 0                | 0              | 0                 | |
| myScheduler | firstSimpleTask   | DEFAULT   | NULL        | zh.maven.SQuartz.task.SimpleFirstTask   | 0          | 0                | 0              | 0                 | |
| myScheduler | firstTask         | DEFAULT   | NULL        | zh.maven.SQuartz.task.FirstTask         | 0          | 0                | 0              | 0                 | |
+-------------+-------------------+-----------+-------------+-----------------------------------------+------------+------------------+----------------+-------------------+----------+

JOB_DATA存放的就是定義task時指定的jobDataMap屬性,所以此屬性需要實現Serializable接口,方便持久化到數據庫;

8.qrtz_calendars

Quartz爲我們提供了日曆的功能,可以自己定義一個時間段,可以控制觸發器在這個時間段內觸發或者不觸發;現在提供6種類型:AnnualCalendar,CronCalendar,DailyCalendar,HolidayCalendar,MonthlyCalendar,WeeklyCalendar;以下使用CronCalendar爲例:

<bean id="firstCalendar" class="org.quartz.impl.calendar.CronCalendar">
    <constructor-arg value="0/5 * * ? * *"></constructor-arg>
</bean>
<bean id="firstCronTrigger"
    class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
    <property name="jobDetail" ref="firstTask" />
    <property name="cronExpression" value="0/6 * * ? * *" />
    <property name="group" value="firstCronGroup"></property>
    <property name="calendarName" value="firstCalendar"></property>
</bean>
<bean id="scheduler"
    class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
    <property name="schedulerName" value="myScheduler"></property>
    <property name="dataSource" ref="dataSource" />
    <property name="configLocation" value="classpath:quartz.properties" />
    <property name="triggers">
        <list>
            <ref bean="firstCronTrigger" />
        </list>
    </property>
    <property name="calendars">
        <map>
            <entry key="firstCalendar" value-ref="firstCalendar"></entry>
        </map>
    </property>
</bean>

定義了一個排除每隔5秒的CronCalendar,然後在firstCronTrigger中指定了calendarName,並且需要在SchedulerFactoryBean中定義calendars;因爲firstCronTrigger每6秒執行一次,而CronCalendar排除每隔5秒,所以會出現firstCronTrigger在第5次觸發的時候需要等待12秒,結果如下:

20180419 15:09:06---start FirstService
20180419 15:09:08---end FirstService
20180419 15:09:12---start FirstService
20180419 15:09:14---end FirstService
20180419 15:09:18---start FirstService
20180419 15:09:20---end FirstService
20180419 15:09:24---start FirstService
20180419 15:09:26---end FirstService
20180419 15:09:36---start FirstService
20180419 15:09:38---end FirstService

查詢保存在數據中的CronCalendar:

mysql> select * from qrtz_calendars;
+-------------+---------------+----------+
| SCHED_NAME  | CALENDAR_NAME | CALENDAR |
+-------------+---------------+----------+
| myScheduler | firstCalendar | |
+-------------+---------------+----------+

CALENDAR存放的是CronCalendar序列化之後的數據;

9.qrtz_paused_trigger_grps

存放暫停掉的觸發器,測試手動暫停firstCronTrigger,代碼如下:

public class App {
    public static void main(String[] args) {
        final AbstractApplicationContext context = new ClassPathXmlApplicationContext("quartz.xml");
        final StdScheduler scheduler = (StdScheduler) context.getBean("scheduler");
        try {
            Thread.sleep(4000);
            scheduler.pauseTriggers(GroupMatcher.triggerGroupEquals("firstCronGroup"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

啓動之後延遲4秒後暫停firstCronTrigger,這裏傳遞的參數group,然後查看數據庫:

mysql> select * from qrtz_paused_trigger_grps;
+-------------+----------------+
| SCHED_NAME  | TRIGGER_GROUP  |
+-------------+----------------+
| myScheduler | firstCronGroup |
+-------------+----------------+

因爲已經入庫,所以重啓之後firstCronGroup還是處於暫停狀態,firstCronTrigger不會運行;

10.qrtz_scheduler_state

存儲所有節點的scheduler,會定期檢查scheduler是否失效,啓動多個scheduler,查詢數據庫:

mysql> select * from qrtz_scheduler_state;
+-------------+---------------------------+-------------------+------------------+
| SCHED_NAME  | INSTANCE_NAME             | LAST_CHECKIN_TIME | CHECKIN_INTERVAL |
+-------------+---------------------------+-------------------+------------------+
| myScheduler | NJD9YZGJ2-PC1524209095408 |     1524209113973 |             1000 |
| myScheduler | NJD9YZGJ2-PC1524209097649 |     1524209113918 |             1000 |
+-------------+---------------------------+-------------------+------------------+

記錄了最後最新的檢查時間,在quartz.properties中設置了CHECKIN_INTERVAL爲1000,也就是每秒檢查一次;

11.qrtz_locks

Quartz提供的鎖表,爲多個節點調度提供分佈式鎖,實現分佈式調度,默認有2個鎖:

mysql> select * from qrtz_locks;
+-------------+----------------+
| SCHED_NAME  | LOCK_NAME      |
+-------------+----------------+
| myScheduler | STATE_ACCESS   |
| myScheduler | TRIGGER_ACCESS |
+-------------+----------------+

STATE_ACCESS主要用在scheduler定期檢查是否失效的時候,保證只有一個節點去處理已經失效的scheduler;
TRIGGER_ACCESS主要用在TRIGGER被調度的時候,保證只有一個節點去執行調度;

總結

本文對這11張表做了簡要的分析,介紹了每張表具體是用來存儲什麼的,並且給了簡單的實例;其實如果要實現一個trigger的管理系統,其實也就是對這幾張表的維護。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章