定时任务看起来简单:到点执行一段代码。但生产环境里的任务会遇到重试、超时、部分成功、机器重启、第三方接口限流和补数。真正的标准不是“跑过一次”,而是重复执行、失败重试、补跑历史数据时,结果仍然正确。
我的判断: XXL-Job 只是触发器,真正决定任务质量的是业务状态表、幂等写入、补偿入口和可追踪日志。调度成功不等于业务成功。
任务入参要可追踪
媒体花费拉取、BI 分群计算、报表预计算,都应该明确任务参数:项目、账号、日期范围、数据类型、批次号。日志里也要带这些字段,否则排查只能靠猜。
写入必须幂等
任务结果入库时要有稳定唯一键。比如广告数据可以用媒体账号、日期、计划 ID、创意 ID 组成业务键;分群结果可以先写临时表,成功后再替换正式表。
超时不是失败的终点
任务超时后,外部系统可能已经处理了一部分。下一次重试不能简单从头新增数据,要支持 upsert、状态检查和断点式补偿。
分片要按业务维度
分片不只是把数据平均切开,更要符合业务边界。按广告主、项目、租户或日期分片,通常比按数据库 ID 取模更容易排查和补跑。
实际场景:媒体数据同步和标签历史版本补算
定时任务在广告平台和 BI 平台里都很常见。广告侧要定时拉取媒体花费、计划状态、创意数据;BI 侧要定时刷新标签、备份历史版本、预计算看板、发送错误数据告警。它们的共同点是:任务经常依赖外部系统或大查询,失败不可避免,补跑也不可避免。
媒体数据同步适合按广告主或媒体账号分片。比如同一天要拉取多个广告主的花费数据,如果某个广告主 token 失效或触发媒体限流,不能让整批任务失败。任务日志里应该记录媒体、广告主、日期、接口类型、成功条数、失败原因。入库时用日期 + 广告主 + 媒体对象 ID 做唯一键,补跑同一天数据时执行 upsert,保证重复执行不产生脏数据。
标签历史版本补算更适合按标签和日期窗口拆分。用户可能选择一段历史日期重新计算,如果一次性全量跑,任务时间长且失败后难恢复。可以把每个日期或每个标签版本作为独立任务,记录状态和耗时;失败时只重跑失败日期。这样运营能看到哪一天成功、哪一天失败,开发也能定位具体 SQL 或数据问题。
XXL-Job 负责调度,但业务状态不能只放在 XXL-Job 日志里。业务系统应该有自己的任务表或日志表,保存参数、状态、执行结果和错误摘要。原因很简单:用户不会去看调度平台日志,业务页面需要展示计算状态;补偿任务也需要根据业务状态决定从哪里继续。
任务超时后也要谨慎。比如媒体接口已经写入一部分数据,但任务整体超时;下一次重跑不能全量 insert,只能 upsert 或按状态继续。BI 计算也是一样,临时结果表写了一半时,不能直接替换正式表。更安全的方式是先写临时表,成功后再切换结果指针,失败则保留旧版本。
一个生产可用的定时任务体系,应该允许重复执行、允许部分失败、允许按业务维度补跑、允许用户查询进度。能做到这些,XXL-Job 才不只是“定时触发器”,而是后端系统稳定性的组成部分。
任务告警也要和业务结果绑定。只告警“任务失败”不够,最好能带上项目、日期、处理对象、失败阶段和重试入口。比如错误数据飞书告警里,如果能直接跳转到任务详情或错误明细,处理效率会比只发一段异常堆栈高很多。定时任务的价值不只是自动执行,还包括失败后让人快速接手。