两种主流格式
Linux/Unix 标准是 5 段:分 时 日 月 周;Quartz(Java/Spring 那一套)是 6 段,前面多了个“秒”。动手前先确认目标平台用哪种,否则表达式整体错位。
特殊字符速查
*任意值;?不指定(只能用于“日”或“周”);,枚举:0 0 1,15 * *每月 1 号和 15 号;-范围:0 9-18 * * *每天 9 到 18 点;/步长:*/5每 5 个单位。
两个最容易踩的坑
- “日”和“周”同时写死:多数实现按“或”处理,1 号或周一都会触发,和直觉相反。想表达“每月仅 1 号”,把“周”写成
?; - 时区:Cron 按服务器时区跑。服务器在 UTC、你在 UTC+8,想本地每天 9 点触发得写
0 1 * * *。
常用表达式
| 需求 | 表达式(5 段) |
|---|---|
| 每分钟 | * * * * * |
| 每 5 分钟 | */5 * * * * |
| 每天 0 点 | 0 0 * * * |
| 工作日 9:30 | 30 9 * * 1-5 |
| 每月 1 号 3 点 | 0 3 1 * ? |
| 每季度首日 | 0 0 1 1,4,7,10 * |
任务没按时跑?按这个顺序查
- 时区:容器默认 UTC,比北京时间晚 8 小时,本地 9 点实际要写
0 1 * * *; - 守护进程:cron 服务没起,或容器重启后 crontab 丢了;
- 字段数错位:平台要 6 段(Quartz)你写了 5 段,反过来也一样;
- 秒位写成 *:
* * * * * *是每秒跑一次,容易把机器压垮; - 没日志:命令后追加
>> /var/log/job.log 2>&1,先确认任务到底有没有被触发。
上线前先验证
别反复部署试错,先用解析工具算出“接下来几次执行时间”,和预期逐条对,几秒就能发现字段写反。跨时区尤其要核对:同一表达式在 UTC 和 UTC+8 下触发时刻差 8 小时。还有三件事要确认:
- 任务具备幂等性,重复跑一次不产生副作用;
- 日志落盘加告警,失败能第一时间发现,而不是等用户来反馈;
- 容器显式设置时区(
TZ=Asia/Shanghai),别靠默认 UTC 猜。
三个真实事故
- “写了 9 点却中午才跑”:容器时区是 UTC,业务却按东八区理解。改成 UTC 对应的
0 1 * * *,或设好TZ再重启; - “每月 1 号跑了,周一又跑一次”:“日”和“周”都写死,触发条件变成“或”。只留一个,另一个写
?; - “手动能跑,cron 不跑”:多半是环境变量和 PATH 不同,cron 不加载你的 shell 配置。脚本里用绝对路径,或显式 source 环境文件。
几个常被问到的问题
“日”和“周”必须有一个写 ? 吗?不强制,但建议写。标准实现里两者同时存在就按“或”触发,容易和直觉拧着。6 段是从秒开始吗?对,Quartz 风格是 秒 分 时 日 月 周 [年],和 Linux 5 段不同。*/5 从 0 分开始吗?会,*/5 等价于 0,5,10,…。支持“每月最后一天”吗?标准 5 段不支持,得靠平台扩展(如 Quartz 的 L)或脚本判断。
动手试试:Cron 表达式解析、时间戳转换