← 返回文章列表

Cron 表达式完全指南:5 段与 6 段、日与周的坑

Cron避坑

两种主流格式

Linux/Unix 标准是 5 段:分 时 日 月 周;Quartz(Java/Spring 那一套)是 6 段,前面多了个“秒”。动手前先确认目标平台用哪种,否则表达式整体错位。

特殊字符速查

  • * 任意值;
  • ? 不指定(只能用于“日”或“周”);
  • , 枚举:0 0 1,15 * * 每月 1 号和 15 号;
  • - 范围:0 9-18 * * * 每天 9 到 18 点;
  • / 步长:*/5 每 5 个单位。

两个最容易踩的坑

  1. “日”和“周”同时写死:多数实现按“或”处理,1 号或周一都会触发,和直觉相反。想表达“每月仅 1 号”,把“周”写成 ?;
  2. 时区:Cron 按服务器时区跑。服务器在 UTC、你在 UTC+8,想本地每天 9 点触发得写 0 1 * * *。

常用表达式

需求表达式(5 段)
每分钟* * * * *
每 5 分钟*/5 * * * *
每天 0 点0 0 * * *
工作日 9:3030 9 * * 1-5
每月 1 号 3 点0 3 1 * ?
每季度首日0 0 1 1,4,7,10 *

任务没按时跑?按这个顺序查

  1. 时区:容器默认 UTC,比北京时间晚 8 小时,本地 9 点实际要写 0 1 * * *;
  2. 守护进程:cron 服务没起,或容器重启后 crontab 丢了;
  3. 字段数错位:平台要 6 段(Quartz)你写了 5 段,反过来也一样;
  4. 秒位写成 *:* * * * * * 是每秒跑一次,容易把机器压垮;
  5. 没日志:命令后追加 >> /var/log/job.log 2>&1,先确认任务到底有没有被触发。

上线前先验证

别反复部署试错,先用解析工具算出“接下来几次执行时间”,和预期逐条对,几秒就能发现字段写反。跨时区尤其要核对:同一表达式在 UTC 和 UTC+8 下触发时刻差 8 小时。还有三件事要确认:

  1. 任务具备幂等性,重复跑一次不产生副作用;
  2. 日志落盘加告警,失败能第一时间发现,而不是等用户来反馈;
  3. 容器显式设置时区(TZ=Asia/Shanghai),别靠默认 UTC 猜。

三个真实事故

  1. “写了 9 点却中午才跑”:容器时区是 UTC,业务却按东八区理解。改成 UTC 对应的 0 1 * * *,或设好 TZ 再重启;
  2. “每月 1 号跑了,周一又跑一次”:“日”和“周”都写死,触发条件变成“或”。只留一个,另一个写 ?;
  3. “手动能跑,cron 不跑”:多半是环境变量和 PATH 不同,cron 不加载你的 shell 配置。脚本里用绝对路径,或显式 source 环境文件。

几个常被问到的问题

“日”和“周”必须有一个写 ? 吗?不强制,但建议写。标准实现里两者同时存在就按“或”触发,容易和直觉拧着。6 段是从秒开始吗?对,Quartz 风格是 秒 分 时 日 月 周 [年],和 Linux 5 段不同。*/5 从 0 分开始吗?会,*/5 等价于 0,5,10,…。支持“每月最后一天”吗?标准 5 段不支持,得靠平台扩展(如 Quartz 的 L)或脚本判断。

动手试试:Cron 表达式解析、时间戳转换