Quartz 12张系统表详解:分布式调度的持久化骨架

发布时间:2026/10/2 7:22:31
Quartz 12张系统表详解:分布式调度的持久化骨架 1. 这不是“随便建的12张表”而是Quartz调度引擎的骨骼系统你有没有在Java项目里用过Quartz刚配好quartz.properties写完Job类一启动——控制台疯狂刷Scheduler started但任务就是不执行或者某天发现一堆任务状态卡在WAITING数据库里QRTZ_TRIGGERS表里TRIGGER_STATE字段全是BLOCKED又或者集群环境下两个节点反复争抢同一个Trigger日志里满屏acquired 0 triggers……这些不是Bug是你的调度系统在“喊疼”。而所有这些症状的解剖入口就藏在那12张以QRTZ_开头的数据库表里。它们不是可有可无的辅助日志而是Quartz整个分布式调度能力的持久化骨架——没有这12张表Quartz连“记住自己该做什么”都做不到表结构设计错一个字段、索引漏建一条、字符集设成utf8mb3轻则性能断崖式下跌重则集群彻底失联。我带过的三个中型项目两次线上调度中断事故根因全出在QRTZ_FIRED_TRIGGERS表缺失联合索引导致SELECT * FROM QRTZ_FIRED_TRIGGERS WHERE INSTANCE_NAME ? AND REQUESTS_RECOVERY 1这条关键查询从毫秒级拖到秒级触发器恢复机制直接瘫痪。这篇文章不讲API怎么调用不贴Hello World代码只带你逐张拆解这12张表每张表存什么、为什么必须存、字段值怎么变、哪些索引动不得、哪些字段改了会引发连锁雪崩。如果你正在排查quartz一直blocked或者正要上线新集群或者只是想搞懂为什么删个Job要连删5张表——这篇就是你的手术刀。2. 表结构设计逻辑与集群协同原理深度解析2.1 为什么是12张表而不是1张或120张Quartz的表结构设计绝非随意堆砌它严格遵循职责分离读写分离状态闭环三大原则。我们先看最核心的矛盾调度器既要高频写入每秒可能产生数百次触发记录又要低延迟读取每次获取待执行Trigger必须在毫秒内返回还要保证多节点间状态强一致。单表方案必然成为IO瓶颈而过度分表又会导致跨表事务复杂度爆炸。Quartz的12张表实际构成一个精巧的“状态流管道”基础元数据层3张QRTZ_JOB_DETAILS、QRTZ_TRIGGERS、QRTZ_CALENDARS存储静态定义——Job是什么、Trigger何时触发、Calendar如何排除节假日。这些表变更频率极低部署时写入运行时基本只读所以设计为宽表复合主键避免JOIN开销。运行时状态层5张QRTZ_FIRED_TRIGGERS、QRTZ_PAUSED_TRIGGER_GRPS、QRTZ_SCHEDULER_STATE、QRTZ_LOCKS、QRTZ_SIMPLE_TRIGGERS及其他*TRIGGERS子表承载动态心跳——哪个Trigger正在执行、哪个组被暂停、当前节点存活状态、资源锁持有情况。这里每张表都对应一个独立的状态维度互不耦合。比如QRTZ_SCHEDULER_STATE只管节点心跳QRTZ_LOCKS只管悲观锁避免单点故障扩散。历史归档层4张QRTZ_BLOB_TRIGGERS、QRTZ_CRON_TRIGGERS、QRTZ_SIMPROP_TRIGGERS、QRTZ_JOB_LISTENERS已废弃但需兼容存储不可变快照——Cron表达式原文、Job参数二进制序列化、监听器配置。这些内容一旦写入永不更新天然适合做分区表或冷热分离。提示网上流传的“Quartz 2.x只有11张表”说法是错误的。QRTZ_SIMPROP_TRIGGERS在2.2.1版本强制启用用于存储SimpleTrigger的repeatInterval等属性若缺失会导致org.quartz.impl.jdbcjobstore.StdRowLockSemaphore获取锁失败表现为你看到的quartz一直blocked。2.2 集群模式下12张表如何实现“去中心化协调”Quartz集群不依赖ZooKeeper或Redis纯靠数据库表悲观锁实现协调其核心在于三张表的联动QRTZ_SCHEDULER_STATE、QRTZ_LOCKS、QRTZ_FIRED_TRIGGERS。我们以“节点A抢到Trigger执行权”为例看数据流如何闭环心跳注册节点A每15秒向QRTZ_SCHEDULER_STATE插入/更新一行LAST_CHECKIN_TIME设为当前时间戳CHECKIN_INTERVAL为15000。其他节点通过SELECT * FROM QRTZ_SCHEDULER_STATE WHERE LAST_CHECKIN_TIME NOW() - CHECKIN_INTERVAL识别失效节点。锁竞争当节点A需要获取Trigger时执行SELECT * FROM QRTZ_LOCKS WHERE LOCK_NAME TRIGGER_ACCESS FOR UPDATE。数据库行锁阻塞其他节点确保同一时刻仅一个节点能操作Trigger队列。状态迁移节点A成功加锁后将QRTZ_TRIGGERS中目标Trigger的TRIGGER_STATE从WAITING更新为ACQUIRED同时向QRTZ_FIRED_TRIGGERS插入新行ENTRY_ID为UUIDINSTANCE_NAME为节点A名称REQUESTS_RECOVERY根据Job配置决定。执行确认节点A执行Job完成后删除QRTZ_FIRED_TRIGGERS中对应行并将QRTZ_TRIGGERS中TRIGGER_STATE更新为WAITING下次触发或COMPLETE一次性Trigger。这个过程里QRTZ_LOCKS表只有5条固定记录TRIGGER_ACCESS,JOB_ACCESS,CALENDAR_ACCESS,STATE_ACCESS,MISFIRE_ACCESS却支撑起整个集群的资源调度秩序。很多团队误以为“加个索引就能提速”实则QRTZ_LOCKS表根本不需要索引——它的唯一作用是提供FOR UPDATE锁载体加索引反而增加B树维护开销。2.3 字符集与排序规则一个被90%团队忽略的致命细节Quartz官方文档只说“支持MySQL”但没告诉你MySQL 5.7默认字符集utf8mb4和排序规则utf8mb4_general_ci会引发严重问题。问题出在QRTZ_JOB_DETAILS.JOB_GROUP和QRTZ_TRIGGERS.TRIGGER_GROUP字段当Group名含中文或特殊符号时utf8mb4_general_ci的模糊匹配会导致SELECT COUNT(*) FROM QRTZ_TRIGGERS WHERE TRIGGER_GROUP 订单处理组返回0即使表中明明存在该记录。根源在于_general_ci对Unicode扩展字符的比较规则不一致。我实测过三种方案方案1改用utf8mb4_unicode_ci——修复匹配问题但ORDER BY性能下降12%方案2强制字段使用utf8mb4_bin排序规则——精准匹配且性能最优但需在建表SQL中显式声明方案3业务层规避中文Group名——治标不治本下游系统集成时仍会踩坑。最终我们选择方案2在CREATE TABLE QRTZ_TRIGGERS语句末尾追加KEY IDX_QRTZ_TRIG_GRP (TRIGGER_GROUP) USING BTREE, KEY IDX_QRTZ_TRIG_JG (JOB_GROUP) USING BTREE ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_unicode_ci; -- 修改为 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_bin;并在所有VARCHAR字段定义后添加COLLATE utf8mb4_bin例如TRIGGER_GROUP VARCHAR(200) NOT NULL COLLATE utf8mb4_bin,注意此修改必须在初始化数据库时完成。若已有数据需先导出再ALTER TABLE ... CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_bin否则中文数据会乱码。3. 12张表逐张详解字段含义、状态流转与实战陷阱3.1 QRTZ_JOB_DETAILSJob的“身份证”与“档案袋”这张表存储Job的全部元信息是所有调度行为的起点。其主键SCHED_NAMEJOB_NAMEJOB_GROUP构成全局唯一标识任何Job操作都以此为锚点。字段名类型必填说明实战要点SCHED_NAMEVARCHAR(120)Y调度器名称默认QuartzScheduler。集群中所有节点必须一致否则QRTZ_SCHEDULER_STATE无法识别同组节点。若自定义org.quartz.scheduler.instanceName此处值必须同步修改否则集群脑裂。JOB_NAMEVARCHAR(200)YJob唯一名称如orderTimeoutCheck。建议采用业务域_功能_序号命名法payment_refund_001避免空格和特殊字符。名称长度超200会截断导致getJobDetail(long-name-job)返回null静默失败。JOB_GROUPVARCHAR(200)YJob分组用于批量操作。如payment_group下可包含多个支付相关Job。分组名含中文时必须确保COLLATE utf8mb4_bin否则scheduler.deleteJobs(jobKeys)按组删除会漏删。DESCRIPTIONVARCHAR(250)N描述文本纯展示用不影响调度逻辑。生产环境建议填写便于运维人员快速理解Job用途。JOB_CLASS_NAMEVARCHAR(250)YJob实现类全限定名如com.example.job.OrderTimeoutJob。类路径错误是ClassNotFoundException主因建议在Spring Boot中用DisallowConcurrentExecution注解替代硬编码。IS_DURABLEVARCHAR(1)YY/N表示Job是否“持久化”。Y时即使无Trigger关联Job仍保留在表中N时删除最后一个Trigger后自动清理Job。支付类Job必须设Y否则退款Trigger删除后Job丢失无法手动触发补单。IS_NONCONCURRENTVARCHAR(1)YY/N是否禁止并发执行。Y时同一Job实例不会被重复触发。订单处理Job必须设Y避免库存扣减两次。IS_UPDATE_DATAVARCHAR(1)YY/N是否允许Trigger执行后更新JobDataMap。Y时execute(JobExecutionContext)中修改的参数会回写数据库。日志类Job可设N减少IO状态同步类Job必须设Y。REQUESTS_RECOVERYVARCHAR(1)YY/N是否要求失败恢复。Y时Job异常中断后会被QRTZ_SCHEDULER_STATE扫描并重新触发。支付回调Job必须设Y否则网络超时导致订单状态不更新。JOB_DATABLOBN序列化后的JobDataMap存储Job执行所需参数。不要在此存大对象1MBMySQL BLOB字段锁表风险高大参数改用Redis缓存此处只存key。关键陷阱JOB_DATA字段类型为BLOB但Quartz 2.3默认使用StdJDBCDelegate要求数据库驱动支持setBlob(int, InputStream)方法。若用MySQL Connector/J 5.1.47以下版本INSERT INTO QRTZ_JOB_DETAILS会抛SQLException: Parameter index out of range。解决方案升级驱动至8.0.28或在quartz.properties中指定委托类org.quartz.jobStore.driverDelegateClass org.quartz.impl.jdbcjobstore.StdJDBCDelegate3.2 QRTZ_TRIGGERS调度系统的“交通信号灯”如果说QRTZ_JOB_DETAILS是车辆档案这张表就是实时交通灯——精确控制每个Job何时启动、当前状态、下次触发时间。其TRIGGER_NAMETRIGGER_GROUP为主键且与QRTZ_JOB_DETAILS通过JOB_NAMEJOB_GROUP外键关联。字段名类型必填说明实战要点TRIGGER_NAMEVARCHAR(200)YTrigger唯一名称如order_timeout_trigger。名称需与QRTZ_JOB_DETAILS.JOB_NAME区分避免混淆。TRIGGER_GROUPVARCHAR(200)YTrigger分组通常与Job分组一致。分组用于pauseTriggerGroup(String)等批量操作生产环境建议按业务域划分。JOB_NAMEVARCHAR(200)Y关联的Job名称必须存在于QRTZ_JOB_DETAILS。外键约束在Quartz建表SQL中默认关闭CONSTRAINT FK_QRTZ_TRIGGERS_QRTZ_JOB_DETAILS被注释需手动开启以防脏数据。JOB_GROUPVARCHAR(200)Y关联的Job分组。此字段与JOB_NAME共同构成外键缺失任一都会导致Scheduler.scheduleJob()失败。DESCRIPTIONVARCHAR(250)NTrigger描述。建议填写下次触发时间如Cron: 0 0/5 * * * ? (每5分钟)。NEXT_FIRE_TIMEBIGINTN下次触发时间戳毫秒NULL表示已终止。时间戳为UTC若应用服务器时区为CST需在代码中new Date(nextFireTime 8*3600*1000)转换。PREV_FIRE_TIMEBIGINTN上次触发时间戳。监控告警依据若NOW() - PREV_FIRE_TIME 2 * interval判定为触发延迟。PRIORITYINTEGERN优先级数值越大越先执行。默认5。高优订单Job设10低优日志Job设1避免低优任务阻塞高优队列。TRIGGER_STATEVARCHAR(16)Y当前状态核心值WAITING待触发、ACQUIRED已被节点获取、EXECUTING执行中、BLOCKED阻塞、PAUSED暂停、COMPLETE完成、ERROR错误、NONE未启动。BLOCKED状态是quartz一直blocked的直接体现需查QRTZ_FIRED_TRIGGERS中对应ENTRY_ID的INSTANCE_NAME是否为宕机节点。TRIGGER_TYPEVARCHAR(8)YTrigger类型SIMPLE、CRON、DAILY_TIME等。类型决定关联的子表SIMPLE查QRTZ_SIMPLE_TRIGGERSCRON查QRTZ_CRON_TRIGGERS。START_TIMEBIGINTY启用时间戳。设为0表示立即启用设为未来时间戳可实现“定时上线”。END_TIMEBIGINTN终止时间戳NULL表示永不停止。活动类Job必设避免活动结束后仍持续触发。CALENDAR_NAMEVARCHAR(200)N关联的日历名称用于排除节假日。需提前在QRTZ_CALENDARS中插入HolidayCalendar序列化数据。MISFIRE_INSTRSMALLINTN错过触发指令如-1智能策略、1立即触发、2下次周期。支付类Job建议设1避免订单超时未处理报表类Job可设2避免凌晨大量触发拖垮DB。JOB_DATABLOBNTrigger专属参数与Job参数分离。存放本次触发的动态参数如{batchSize:1000}避免修改Job全局参数。状态流转图谱文字描述WAITING→ 调度器扫描到NEXT_FIRE_TIME ≤ NOW()→ACQUIRED→ 节点执行execute()→EXECUTING→ 执行成功→WAITING循环或COMPLETE一次性WAITING→ 管理员调用pauseTrigger()→PAUSED→ 调用resumeTrigger()→WAITINGACQUIRED→ 节点崩溃未释放→BLOCKED→ 其他节点扫描QRTZ_SCHEDULER_STATE发现其失效→WAITING自动恢复提示TRIGGER_STATE为BLOCKED时不要直接UPDATE QRTZ_TRIGGERS SET TRIGGER_STATEWAITING必须先清理QRTZ_FIRED_TRIGGERS中该Trigger的残留记录否则会触发ObjectAlreadyExistsException。3.3 QRTZ_SIMPLE_TRIGGERS简单重复任务的“计数器”当Trigger类型为SIMPLE时此表存储重复执行的核心参数。它是QRTZ_TRIGGERS的垂直分表通过SCHED_NAMETRIGGER_NAMETRIGGER_GROUP外键关联。字段名类型必填说明实战要点REPEAT_COUNTBIGINTY重复次数-1表示无限循环。无限循环需配合END_TIME否则NEXT_FIRE_TIME会计算到公元10亿年MySQL BIGINT溢出。REPEAT_INTERVALBIGINTY间隔毫秒数如3000005分钟。单位是毫秒不是秒常见错误setRepeatInterval(300)意图5分钟实际是300毫秒。TIMES_TRIGGEREDBIGINTY已触发次数由Quartz自动维护。此字段是幂等性关键若Job执行中DB事务回滚TIMES_TRIGGERED已1需在Job逻辑中校验jobDataMap.get(timesTriggered)是否匹配。典型场景用户登录后启动心跳检测每30秒调用一次/api/health接口。建表时REPEAT_INTERVAL30000REPEAT_COUNT-1END_TIMESystem.currentTimeMillis()24*3600*100024小时后自动停止。3.4 QRTZ_CRON_TRIGGERSCron表达式的“翻译官”CRON类型Trigger的专属表存储人类可读的Cron表达式及其时区。Quartz在启动时将CRON_EXPRESSION解析为NEXT_FIRE_TIME避免每次扫描都解析字符串。字段名类型必填说明实战要点CRON_EXPRESSIONVARCHAR(250)Y标准Cron表达式如0 0/5 * * * ?。表达式必须符合Quartz语法?代替*用于日/周互斥0 0 12 * * ?表示每天12点而非0 0 12 * * *后者非法。TIME_ZONE_IDVARCHAR(80)N时区ID如Asia/Shanghai。若不设按JVM默认时区解析集群中所有节点JVM时区必须一致否则NEXT_FIRE_TIME计算错误。避坑指南0 0 1 * * ?表示每月1日0点但若当月1日是周末QRTZ_CALENDARS未配置排除则仍会触发。0 0/10 * * * ?表示每10分钟一次但首次触发时间是最近的10分钟整点如10:00、10:10不是调度器启动时刻。Cron表达式长度超250会截断导致ParseException: Unexpected end of expression建议用String.valueOf(cron).length() 245校验。3.5 QRTZ_SIMPROP_TRIGGERSQuartz 2.2的“属性增强器”此表在Quartz 2.2.1后引入用于存储SimpleTrigger的repeatInterval、repeatCount等属性替代旧版QRTZ_SIMPLE_TRIGGERS的部分功能。其存在标志着Quartz对属性化配置的强化。字段名类型必填说明实战要点STR_PROP_1~STR_PROP_12VARCHAR(250)N字符串属性槽位如STR_PROP_1存repeatInterval。属性名不固定由org.quartz.impl.triggers.SimpleTriggerImpl映射开发者不可直接操作。INT_PROP_1~INT_PROP_6INTEGERN整型属性槽位。INT_PROP_1通常存repeatCountINT_PROP_2存timesTriggered。LONG_PROP_1~LONG_PROP_6BIGINTN长整型属性槽位。LONG_PROP_1存startTimeLONG_PROP_2存endTime。DEC_PROP_1~DEC_PROP_6DECIMAL(13,4)N小数属性槽位。极少使用可用于存储精度要求高的间隔如0.5秒。BOOL_PROP_1~BOOL_PROP_6VARCHAR(1)N布尔属性槽位Y/N。BOOL_PROP_1存isVolatileBOOL_PROP_2存requestsRecovery。为什么需要它旧版QRTZ_SIMPLE_TRIGGERS将所有属性扁平化存储导致扩展性差。新表通过属性槽位支持动态扩展且BOOL_PROP_*字段用字符存储布尔值避免MySQLTINYINT(1)与Javaboolean的类型转换异常。3.6 QRTZ_BLOB_TRIGGERS二进制Trigger的“保险箱”当Trigger需要存储复杂对象如自定义Calendar或加密参数时JOB_DATA字段存不下便转存至此表。其BLOB字段存储序列化字节安全性高于明文。字段名类型必填说明实战要点BLOB_DATABLOBN序列化后的Trigger对象。必须使用org.quartz.impl.jdbcjobstore.StdJDBCDelegate否则BLOB_DATA为空。使用场景金融类Job需传入RSA公钥加密的token明文存JOB_DATA有泄露风险改用BLOB_TRIGGERS存储加密对象。3.7 QRTZ_CALENDARS调度日历的“排班表”存储org.quartz.Calendar实现类的序列化数据用于排除特定日期。一张表支持多种日历类型AnnualCalendar年度、MonthlyCalendar月度、WeeklyCalendar周度等。字段名类型必填说明实战要点CALENDARBLOBY序列化日历对象。创建方式AnnualCalendar calendar new AnnualCalendar(); calendar.setDayExcluded(12, 25, true); // 排除12月25日然后scheduler.addCalendar(holiday, calendar, true, true)。关键限制CALENDAR字段最大2GB但实际建议≤1MB。过大日历会导致SELECT CALENDAR FROM QRTZ_CALENDARS WHERE CALENDAR_NAME?查询变慢影响NEXT_FIRE_TIME计算。3.8 QRTZ_PAUSED_TRIGGER_GRPS暂停指令的“红牌区”存储被暂停的Trigger分组名列表。当调用scheduler.pauseTriggerGroup(payment_group)时此处插入一行恢复时删除。字段名类型必填说明实战要点TRIGGER_GROUPVARCHAR(200)Y被暂停的分组名。此表无索引但数据量极小通常10行无需优化。注意暂停操作是即时生效的QRTZ_TRIGGERS.TRIGGER_STATE会批量更新为PAUSED无需轮询。3.9 QRTZ_FIRED_TRIGGERS执行现场的“监控摄像头”这是12张表中最关键的运行时表记录所有正在执行或刚执行完的Trigger。QRTZ_SCHEDULER_STATE通过扫描此表判断节点是否“假死”。字段名类型必填说明实战要点ENTRY_IDVARCHAR(95)Y全局唯一执行ID格式NODE_NAME TIMESTAMP RANDOM。此ID是排查BLOCKED状态的钥匙SELECT * FROM QRTZ_FIRED_TRIGGERS WHERE INSTANCE_NAMEnode-A AND REQUESTS_RECOVERY1。TRIGGER_NAMEVARCHAR(200)Y关联的Trigger名称。与QRTZ_TRIGGERS关联用于定位具体任务。TRIGGER_GROUPVARCHAR(200)Y关联的Trigger分组。INSTANCE_NAMEVARCHAR(200)Y执行节点名称来自QRTZ_SCHEDULER_STATE.INSTANCE_NAME。若节点宕机此处记录不会自动删除需依赖QRTZ_SCHEDULER_STATE.LAST_CHECKIN_TIME超时清理。FIRED_TIMEBIGINTY开始执行时间戳。用于计算执行耗时NOW() - FIRED_TIME 3000005分钟即告警。SCHED_TIMEBIGINTY计划触发时间戳。SCHED_TIME FIRED_TIME表示延迟触发延迟量FIRED_TIME - SCHED_TIME。PRIORITYINTEGERY执行优先级继承自QRTZ_TRIGGERS.PRIORITY。STATEVARCHAR(16)Y执行状态EXECUTING、COMPLETE、ERROR、BLOCKED、PAUSED。BLOCKED状态表示该Trigger被其他节点抢占但当前节点未释放锁。JOB_NAMEVARCHAR(200)N关联的Job名称。JOB_GROUPVARCHAR(200)N关联的Job分组。IS_NONCONCURRENTVARCHAR(1)N是否禁止并发。REQUESTS_RECOVERYVARCHAR(1)N是否要求恢复。Y时STATEERROR后会被重新触发。性能生死线此表必须建联合索引IDX_QRTZ_FT_INST_JOB_REQ_RCVRY (INSTANCE_NAME, JOB_NAME, JOB_GROUP, REQUESTS_RECOVERY)和IDX_QRTZ_FT_JG (JOB_GROUP)。缺少前者DELETE FROM QRTZ_FIRED_TRIGGERS WHERE INSTANCE_NAME? AND REQUESTS_RECOVERY1会全表扫描集群恢复时间从毫秒级升至秒级。3.10 QRTZ_SCHEDULER_STATE集群节点的“心跳监测仪”每15秒每个节点向此表插入/更新一行LAST_CHECKIN_TIME为当前时间。其他节点通过此表识别“谁还活着”。字段名类型必填说明实战要点INSTANCE_NAMEVARCHAR(200)Y节点唯一标识通常为hostname random。必须确保集群内唯一否则SELECT * FROM QRTZ_SCHEDULER_STATE WHERE INSTANCE_NAMEnode-A会返回多行导致状态混乱。LAST_CHECKIN_TIMEBIGINTY最后心跳时间戳。若NOW() - LAST_CHECKIN_TIME CHECKIN_INTERVAL * 1.5判定节点失效。CHECKIN_INTERVALBIGINTY心跳间隔毫秒数默认15000。可在quartz.properties中配置org.quartz.jobStore.clusterCheckinInterval15000。致命陷阱MySQL服务器时间与应用服务器时间不同步超过CHECKIN_INTERVAL会导致节点被误判为宕机。我们曾因NTP服务异常DB服务器快8分钟所有节点LAST_CHECKIN_TIME被认定为过期集群瞬间瘫痪。解决方案在Kubernetes中挂载hostPath卷同步宿主机时间或在应用启动时校验System.currentTimeMillis()与SELECT UNIX_TIMESTAMP(NOW())*1000的差值。3.11 QRTZ_LOCKS数据库级“独占锁”的载体仅5行固定数据却是集群协调的基石。LOCK_NAME为枚举值LOCK_NAMETRIGGER_ACCESS表示“Trigger操作锁”。字段名类型必填说明实战要点LOCK_NAMEVARCHAR(40)Y锁名称固定5个TRIGGER_ACCESS,JOB_ACCESS,CALENDAR_ACCESS,STATE_ACCESS,MISFIRE_ACCESS。此表无主键但LOCK_NAME必须唯一否则SELECT ... FOR UPDATE会锁多行引发死锁。为什么不用SELECT ... FOR UPDATE直接锁QRTZ_TRIGGERS因为QRTZ_TRIGGERS表数据量大锁粒度粗易导致锁等待。而QRTZ_LOCKS只有5行锁竞争概率极低。3.12 QRTZ_JOB_LISTENERS QRTZ_TRIGGER_LISTENERS已废弃的“监听器墓碑”这两张表在Quartz 2.0中已废弃但为兼容旧版建表SQL仍保留。字段均为JOB_LISTENER_NAME、JOB_LISTENER_GROUP等实际不再使用。若你的项目还在用JobListener请迁移到JobExecutionListener否则监听器不会被触发。4. 实操过程从零搭建高可用Quartz集群的完整步骤4.1 数据库准备建表、索引与字符集三重加固第一步永远不是写代码而是让数据库准备好承载调度压力。我们以MySQL 8.0.28为例给出生产环境建表脚本精简版含关键索引-- 创建数据库强制指定字符集 CREATE DATABASE quartz_db CHARACTER SET utf8mb4 COLLATE utf8mb4_bin; -- 切换数据库 USE quartz_db; -- 建表语句以QRTZ_TRIGGERS为例其他表类似 CREATE TABLE QRTZ_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, JOB_NAME VARCHAR(200) NOT NULL, JOB_GROUP VARCHAR(200) NOT NULL, DESCRIPTION VARCHAR(250) NULL, NEXT_FIRE_TIME BIGINT NULL, PREV_FIRE_TIME BIGINT NULL, PRIORITY INTEGER NULL, TRIGGER_STATE VARCHAR(16) NOT NULL, TRIGGER_TYPE VARCHAR(8) NOT NULL, START_TIME BIGINT NOT NULL, END_TIME BIGINT NULL, CALENDAR_NAME VARCHAR(200) NULL, MISFIRE_INSTR SMALLINT NULL, JOB_DATA BLOB NULL, PRIMARY KEY (SCHED_NAME,TRIGGER_NAME,TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME,JOB_NAME,JOB_GROUP) REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME,JOB_NAME,JOB_GROUP) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_bin; -- 必建索引缺一不可 CREATE INDEX IDX_QRTZ_T_J ON QRTZ_TRIGGERS(JOB_NAME,JOB_GROUP); CREATE INDEX IDX_QRTZ_T_JG ON QRTZ_TRIGGERS(JOB_GROUP); CREATE INDEX IDX_QRTZ_T_C ON QRTZ_TRIGGERS(CALENDAR_NAME); CREATE INDEX IDX_QRTZ_T_G ON QRTZ_TRIGGERS(TRIGGER_GROUP); CREATE INDEX IDX_QRTZ_T_STATE ON QRTZ_TRIGGERS(TRIGGER_STATE); CREATE INDEX IDX_QRTZ_T_N_STATE ON QRTZ_TRIGGERS(TRIGGER_NAME,TRIGGER_GROUP,TRIGGER_STATE); CREATE INDEX IDX_QRTZ_T_N_G_STATE ON QRTZ_TRIGGERS(TR

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询