Cron 表达式(Cron expression)是一串用空格分隔的字段,用来描述“在哪些日历时刻执行一次任务”。它最早由 Unix 的 cron 守护进程(cron daemon)使用,如今几乎所有调度系统——Linux 系统任务、Kubernetes 的 CronJob、各类工作流引擎、以及给 Agent 配置的定时触发——都沿用同一套写法。
从左往右,五个筛子
标准五字段格式依次是:分钟、小时、日、月、星期。把它想成五道筛子,时间从这五道筛子里逐层漏下去,全部命中才触发一次。每一格可以是 *(任意)、具体数字、逗号列举(1,15)、范围(1-5)或步长(*/15)。
30 7 * * 1-5:周一到周五的 7:30。
*/15 9-18 * * *:每天 9 点到 18 点之间,每 15 分钟一次。
三处最容易读错的地方:
- 星期从 0 开始,0 和 7 都代表周日。而 Quartz 这类 Java 生态实现是 1 代表周日、7 代表周六,跨框架抄表达式极易错位。
- “日”和“星期”是“或”的关系。
0 0 1 * 1不是“每月 1 号且是周一”,而是“每月 1 号,以及每个周一”。 - 字段数量不统一。Quartz、Spring 常用六字段,最前面多一个“秒”;Linux 系统 cron 是五字段。少写或多写一格,语义完全不同。
它和“每隔一段时间”不是一回事
| 写法 | 语义 | 例子 |
|---|---|---|
| Cron 表达式 | 对齐日历时刻,固定时间点触发 | 0 */5 * * * 永远是整 5 分、10 分…… |
| 间隔定时器 | 从启动时刻起算,随时间漂移 | 10:03 启动,每 5 分钟 → 10:08、10:13 |
| 事件触发 | 由外部事件驱动,无固定节奏 | 收到消息就处理 |
为什么定时跑的 Agent 总在错的时间醒
最常见的原因是时区。调度器默认读宿主机时区,而容器镜像里通常是 UTC。你在代码里写“每天 9 点”,在 UTC+8 的环境里实际是下午 5 点跑。夏令时切换时,还会出现某个时刻不存在、或同一时刻被触发两次。
第二常见的是副本重复触发。服务部署了三个实例,每个实例的调度器都读到同一条表达式,于是任务跑三遍——数据重复写入、消息重复发送。
第三是错过与堆积。机器休眠、重启、或上一次任务还没跑完,调度器要么跳过,要么排队补跑,表现就是“有时候没跑,有时候连着跑”。
三条实用建议:表达式旁边显式写死时区,不要依赖默认值;用现成的解析库(如 croniter、cron-parser)算出未来几次触发时间,打印出来自检一遍;任务本身做成幂等,多副本场景加分布式锁或只让一个实例持有调度权。
各实现对字段范围和扩展语法的定义略有差异,具体以所用调度器的官方文档为准。
