返回知识库

数据与服务 · 运行与可靠性

定时任务Cron

cron 用一条 5 段时间表达式 + 固定时区反复触发任务;任务要幂等、防重叠、失败必告警,长时或实时任务用 worker / 队列。

cron 时间表实验台:拼表达式、推进时间,亲手触发一次清理

cron 用一条 5 段表达式 + 固定时区决定“几点在哪个时区跑”。在这里拼表达式,点「推进 1 小时」在模拟一周里看它何时触发;叠「防重叠锁 / 慢任务」看重叠,叠「注入运行失败」看告警为什么必须发出——这些都是真实 cron 任务最常踩的坑。

现实里 crontab -e(或 Kubernetes CronJob)写一条 5 段表达式,调度器按它反复触发——这是第 0 步。这里拼表达式、点「推进 1 小时」在模拟一周里看它何时触发;叠「防重叠锁 / 慢任务」看重叠,叠「注入运行失败」看告警为什么必须发出。

0 02 * * *每天 02:00时区 Asia/Shanghai(声明时区;夏令时会让“每天 02:00”在切换日少跑或多跑一次)
预设
小时字段02步进会离开预设变成自定义(如每天 03:00)。
当前模拟时间周一 00:00Asia/Shanghai
下一次将触发周一 02:00本周共 7 次 · 已触发 0 · 异常 0
本次运行记录(RUNNING / SUCCESS / FAILED / SKIPPED / DOUBLE,最新在上)
还没有触发。点主操作「推进 1 小时」推进模拟时钟;命中表达式(如「每天 02:00」到周二 02:00)就会生成一条 run 卡。
失败告警(cron 失败必须 page 值班,连 monitoring / log)当前告警 0
静默态:还没有告警。点「注入运行失败」再看「推进到下一次触发」——失败若不告警,2am 的错要等白天用户才发现。

当前 周一 00:00,时区 Asia/Shanghai。表达式「每天 02:00」本周将触发 7 次,下一次 周一 02:00。下一步:点主操作「推进 1 小时」推进模拟时钟,命中触发时刻会生成一条 run 卡。

提示:Tab 在按钮 / 复选框间移动,Enter / 空格触发。重点三组对照——「推进到下一次触发」看 run 卡 RUNNING→SUCCESS;开「慢任务」+ 关「防重叠锁」再触发看 DOUBLE,再勾回锁看 SKIPPED;点「注入运行失败」看告警卡为什么必须发出。

实验台暴露的 cron 规则

面板把表达式、时区、防重叠和告警演给你看了,这里命名清楚,并补无法直接操作的部分。

  • 5 段表达式:分 时 日 月 周。面板顶部的五块就是 0 2 * * * = “每天 02:00”:分=0、时=2,日/月/周是 * 代表“每个”。* 是“都行”,数字是“正好这个”,*/5 是“每 5 分钟”,1-5 是“周一到周五”。
  • 必须声明时区。“每天 02:00”在没有时区时是无意义的——服务器跑在 UTC,02:00 就是业务侧的上午。面板右上角写死 Asia/Shanghai;夏令时切换日,欧洲时区的“每天 02:00”会少跑或多跑一次,部署在哪个时区就按哪个时区声明。
  • 幂等 + 防重叠。cron 不知道上一轮有没有跑完:任务执行时间超过间隔时,加锁跳过(面板的 SKIPPED)比两份并发写同一张表(面板的 DOUBLE·并发写入)安全;任务本身也要幂等,重跑或补跑不产生重复副作用(连 idempotency)。
  • 失败必须告警。cron 最常在凌晨无人值守跑,失败若不 page 人,2am 的错要等白天用户发现数据没清才知道。面板的「注入运行失败」→ FAILED run 卡 → 告警卡,告警带 job id 和触发时间,沿 log 复现这一次(连 monitoring、log)。
  • 周期批处理,不是长时 / 实时。cron 适合“每天清理、每周汇总、每小时轮转”这类定时批;不适合长时跑满的流式任务或需要毫秒延迟的实时任务——那些用 worker / 消息队列(连 worker、queue)。

什么时候用 cron,什么时候换别的

从“这件事需不需要按时间表反复跑”出发,不是所有后台活都该塞进 cron。

  • 适合:定时清理过期数据、每天发汇总邮件、每小时对账、每周备份、定期刷缓存——固定时间表 + 批量 + 可重跑。
  • 不适合长时任务:单次跑几小时的计算,用 worker + 队列拆成可恢复的后台作业,cron 只负责“到点把它投出去”。
  • 不适合实时:用户下单后 30 秒内触发的动作,走消息队列 / 延迟队列,cron 最小颗粒是分钟,且不等业务事件。
  • 不适合强依赖外部事件:监听 webhook、文件上传完成这类事件驱动,用事件 + worker,而不是“每分钟轮询一次”。

从一条需求到可运维的 cron 任务:最短链

一条凌晨清理任务,从写表达式到能告警,只走这几步。

  1. 写 5 段表达式并声明时区:如 0 2 * * * + Asia/Shanghai,避免“9 点是哪个 9 点”的歧义。
  2. 设最长执行时间(timeout)+ 防重叠锁:超过间隔时跳过本次或排队,不让两份并发写同一资源。
  3. 任务体做成幂等:重跑或补跑不重复删 / 不重复扣,按业务键查重(连 idempotency)。
  4. 每次运行记 job id、开始 / 结束时间、处理数量、状态:失败可追查,成功可审计(连 log)。
  5. 失败触发去重告警 + 留 runbook:告警带规则、job id、触发时间和回滚 / 重试入口(连 monitoring)。

同一份凌晨清理:5 分钟介入,还是被用户先发现

只改一处关键选择,结果差很多。这是 cron 设计里最该记住的一组对照。

正例 · 声明时区 + 防重叠锁 + 失败告警慢任务被锁跳过,失败 5 分钟有人接0 2 * * *
tz: Asia/Shanghai · timeout 10m · concurrencyPolicy: Forbid · 失败 → page 值班

清理跑了 90 分钟时,02:00 的下一轮被锁跳过(SKIPPED),不会两份并发删;某次抛异常 → 告警带 job id 和触发时间,值班沿 log 5 分钟定位到 DB 连接池打满,回滚连接配置。时区写死,部署到任何环境都跑在业务侧凌晨。

反例 · 无时区 + 无锁 + 静默服务器 UTC,慢任务并发跑坏数据,2am 失败无人知0 2 * * *
# 无时区 · 无 timeout · 无告警

服务器是 UTC,02:00 实际是业务侧 10:00——清理在用户高峰期跑;任务偶尔跑超 1 小时,两份并发删同一批链接,把还在用的也删了;2am 一次 DB 超时失败,没人收到通知,第二天用户反馈“我的链接没了”,团队比用户晚 6 小时知道。

快速自测

你的 cron 清理任务跑在 Asia/Shanghai,表达式 `0 2 * * *`,但最近两次执行都接近 1 小时。今晚最该先做的两件事是?

继续查证

术语的技术定义和行为以这些一手或权威资料为准。

下一步学

和本知识点经常一起出现的概念。