3. Quartz 核心表详解
Quartz 调度框架在关系型数据库中的核心表结构是索引设计与连接池水位巡检的基础。本章将详细拆解 QRTZ_JOB_DETAILS、QRTZ_TRIGGERS、QRTZ_CRON_TRIGGERS 和 QRTZ_SIMPLE_TRIGGERS 四张核心表的字段含义、主键与外键关系,以及它们在调度生命周期中的角色。
巡检视角提示: 理解这些表的字段类型与索引分布,是后续分析慢查询、锁等待以及连接池水位异常的关键前置知识。
3.1 核心表关系总览
Quartz 的调度数据模型以 Job(任务) 和 Trigger(触发器) 为中心。一张 Job 可以被多个 Trigger 关联,但一个 Trigger 只能绑定一个 Job。下图展示了四张核心表之间的主外键关系与数据流向。
3.2 QRTZ_JOB_DETAILS — 任务定义表
该表存储每个调度任务的元数据定义,包括任务类全限定名、任务描述以及持久化的 JobDataMap 数据。一条记录对应一个唯一的 Job 定义。
| 字段名 | 类型 | 主键/索引 | 含义说明 |
|---|---|---|---|
| SCHED_NAME | VARCHAR(120) | PK (1) | 调度器实例名称,用于区分多调度器部署 |
| JOB_NAME | VARCHAR(200) | PK (2) | 任务名称,与 JOB_GROUP 联合唯一 |
| JOB_GROUP | VARCHAR(200) | PK (3) | 任务分组,用于逻辑归类 |
| DESCRIPTION | VARCHAR(250) | — | 任务描述文本 |
| JOB_CLASS_NAME | VARCHAR(250) | — | 实现 Job 接口的类全限定名,Quartz 通过反射实例化 |
| IS_DURABLE | VARCHAR(1) | — | 是否持久化('Y'/'N'),无触发器关联时是否保留任务 |
| IS_NONCONCURRENT | VARCHAR(1) | — | 是否禁止并发执行('Y'/'N'),防止同一任务重叠执行 |
| IS_UPDATE_DATA | VARCHAR(1) | — | 每次执行后是否更新 JobDataMap |
| REQUESTS_RECOVERY | VARCHAR(1) | — | 调度器恢复时是否重新执行未完成的任务 |
| JOB_DATA | BLOB | — | 序列化的 JobDataMap,存储自定义参数 |
索引巡检要点: 该表主键为 (SCHED_NAME, JOB_NAME, JOB_GROUP)。查询时若仅按 JOB_GROUP 过滤,需确认是否有二级索引覆盖。JOB_DATA 字段为 BLOB 类型,避免在 WHERE 条件中直接引用。
3.3 QRTZ_TRIGGERS — 触发器主表
这是 Quartz 调度体系中最核心的表,记录所有触发器的通用属性,包括状态、下次触发时间、上次触发时间,以及关联的 Job 信息。每个触发器在此表中有一条记录,并可能对应一张子表(Cron 或 Simple)。
| 字段名 | 类型 | 主键/索引 | 含义说明 |
|---|---|---|---|
| SCHED_NAME | VARCHAR(120) | PK (1) | 调度器实例名称 |
| TRIGGER_NAME | VARCHAR(200) | PK (2) | 触发器名称 |
| TRIGGER_GROUP | VARCHAR(200) | PK (3) | 触发器分组 |
| JOB_NAME | VARCHAR(200) | FK | 关联的任务名称,外键指向 QRTZ_JOB_DETAILS |
| JOB_GROUP | VARCHAR(200) | FK | 关联的任务分组 |
| DESCRIPTION | VARCHAR(250) | — | 触发器描述 |
| NEXT_FIRE_TIME | BIGINT | 索引 | 下次计划触发时间(毫秒时间戳),调度器轮询核心字段 |
| PREV_FIRE_TIME | BIGINT | — | 上次实际触发时间 |
| PRIORITY | INTEGER | — | 优先级,多个触发器同时到期时决定执行顺序 |
| TRIGGER_STATE | VARCHAR(16) | 索引 | 触发器状态:WAITING, ACQUIRED, EXECUTING, COMPLETE, BLOCKED, ERROR, PAUSED |
| TRIGGER_TYPE | VARCHAR(8) | — | 触发器类型:CRON 或 SIMPLE |
| START_TIME | BIGINT | — | 触发器生效起始时间 |
| END_TIME | BIGINT | — | 触发器失效结束时间,NULL 表示永不过期 |
| CALENDAR_NAME | VARCHAR(200) | — | 关联的日历名称,用于排除特定时间段 |
| MISFIRE_INSTR | INTEGER | — | 错过触发后的处理策略(如立即执行、等待下次等) |
| JOB_DATA | BLOB | — | 触发器级别的 JobDataMap,覆盖任务级别的同名参数 |
连接池水位关键字段: NEXT_FIRE_TIME 和 TRIGGER_STATE 是 Quartz 调度线程轮询频率最高的字段。如果这两列缺少索引,会导致全表扫描,直接拉高数据库连接池的活跃连接数,引发水位告警。
3.4 QRTZ_CRON_TRIGGERS — Cron 表达式触发器子表
当 QRTZ_TRIGGERS 中 TRIGGER_TYPE 为 'CRON' 时,该表存储对应的 Cron 表达式和时区信息。它与 QRTZ_TRIGGERS 形成 1:1 的关系。
| 字段名 | 类型 | 主键/索引 | 含义说明 |
|---|---|---|---|
| SCHED_NAME | VARCHAR(120) | PK (1) | 调度器实例名称 |
| TRIGGER_NAME | VARCHAR(200) | PK (2) | 触发器名称,与 QRTZ_TRIGGERS 主键一致 |
| TRIGGER_GROUP | VARCHAR(200) | PK (3) | 触发器分组 |
| CRON_EXPRESSION | VARCHAR(120) | — | Cron 表达式,如 "0 0/5 * * * ?" |
| TIME_ZONE_ID | VARCHAR(80) | — | 时区 ID,如 "Asia/Shanghai" |
巡检提示: CRON_EXPRESSION 字段虽然不长,但若频繁按表达式内容查询(如统计某类 Cron 模式的触发器数量),建议在该列上建立二级索引。默认情况下,该表仅通过主键关联查询。
3.5 QRTZ_SIMPLE_TRIGGERS — 简单触发器子表
当 QRTZ_TRIGGERS 中 TRIGGER_TYPE 为 'SIMPLE' 时,该表存储重复次数和重复间隔。适用于固定频率、固定次数的调度场景。
| 字段名 | 类型 | 主键/索引 | 含义说明 |
|---|---|---|---|
| SCHED_NAME | VARCHAR(120) | PK (1) | 调度器实例名称 |
| TRIGGER_NAME | VARCHAR(200) | PK (2) | 触发器名称 |
| TRIGGER_GROUP | VARCHAR(200) | PK (3) | 触发器分组 |
| REPEAT_COUNT | BIGINT | — | 重复执行次数,0 表示仅执行一次,-1 表示无限重复 |
| REPEAT_INTERVAL | BIGINT | — | 重复间隔(毫秒) |
| TIMES_TRIGGERED | BIGINT | — | 已触发次数,用于判断是否达到 REPEAT_COUNT |
使用场景对比: SIMPLE 触发器适合固定延迟的轮询任务(如每 10 秒检查一次文件),而 CRON 触发器适合日历时间点触发(如每天凌晨 2 点执行)。在索引设计上,两张子表均以主键关联,无需额外索引。
3.6 四张表字段对比速查
下表从 索引选择 和 连接池影响 两个维度,对四张核心表的关键字段进行横向对比。
| 表名 | 核心查询字段 | 高频更新字段 | 索引建议 | 连接池影响 |
|---|---|---|---|---|
| QRTZ_JOB_DETAILS | JOB_NAME, JOB_GROUP | JOB_DATA(较少) | 主键索引即可 | 低,仅任务注册/查询时访问 |
| QRTZ_TRIGGERS | NEXT_FIRE_TIME, TRIGGER_STATE | NEXT_FIRE_TIME, TRIGGER_STATE | 必须 建 (TRIGGER_STATE, NEXT_FIRE_TIME) 复合索引 | 高,调度线程每秒轮询 |
| QRTZ_CRON_TRIGGERS | CRON_EXPRESSION | 极少 | 主键索引即可 | 低,仅触发器加载时访问 |
| QRTZ_SIMPLE_TRIGGERS | REPEAT_COUNT, TIMES_TRIGGERED | TIMES_TRIGGERED | 主键索引即可 | 中,每次触发后更新已触发次数 |
核心结论: QRTZ_TRIGGERS 表是连接池压力的主要来源。缺少 (TRIGGER_STATE, NEXT_FIRE_TIME) 复合索引时,调度器轮询会触发全表扫描,导致连接池活跃连接数飙升,甚至引发数据库连接耗尽。后续章节将专门讲解如何通过索引优化降低水位。