你有没有遇到过这种事:明明写的是"每天早上9点跑一次",结果任务在下午5点才动,或者干脆没跑。翻日志、查代码,最后发现问题出在那串只有五个字符的表达式上。
Cron 是 Unix 世界里最经典的定时任务调度器,它的表达式语法早就跑出了 Unix 的边界——CI 流水线、云调度器、任务队列,几乎都认这套写法。语法足够紧凑,也足够容易写错。这篇就把这五个字段拆开讲清楚。
五个字段,从左到右读
一条标准的 cron 表达式由五个空格分隔的字段组成,从左到右依次是:
- 分钟:0-59
- 小时:0-23
- 日期(几号):1-31
- 月份:1-12
- 星期几:0-6,其中 0 代表周日
当当前时间同时匹配这五个字段时,任务就会执行。字段里的*表示"该位置取所有可能的值"。
比如* * * * *,五个字段全是星号,意思就是每分钟都跑一次。
四个特殊字符,撑起所有写法
除了星号,还有三个符号需要记住:
- 用来分隔列表。写 1,15,就是第 1 天和第 15 天。
- 用来定义范围。写 1-5,就是 1 到 5;放在星期几字段里,就是周一到周五。
- 用来定义步长。写 */15 表示每 15 个单位执行一次;写 10-40/10,得到的是 10、20、30、40。
把这几个符号组合起来,常见的调度需求基本都能覆盖:
- */5 * * * *:每 5 分钟跑一次
- :每个整点跑一次
- 0 9 * * 1-5:工作日早上 9 点跑
- 30 2 * * 0:每周日凌晨 2 点 30 分跑
- 0 0 1 * *:每月 1 号零点跑
- 0 0 1 1 *:一年只跑一次,1 月 1 日零点
- 15 14 1 * *:每月 1 号 14 点 15 分跑
坑一:时区不是你以为的那个
Cron 运行在机器或服务所在的时区里,而这个时区经常是 UTC。你写下的"早上 9 点",很可能指的是 UTC 早上 9 点,而不是你本地时间的早上 9 点。
云调度器通常允许显式设置时区,但在设置之前,别默认它跟你的本地时间一致。
坑二:日期和星期几同时限制,是"或"不是"与"
在经典的 Vixie cron 里,如果日期字段和星期几字段同时被限制,任务会在两者任意一个匹配时执行,而不是两个都匹配才执行。
写0 0 13 * 5,它会在每个 13 号执行,也会在每个周五执行,而不是只在"13 号恰好是周五"那天执行。其他调度器的行为可能不一样,用之前得看它们的文档。
坑三:方言差异比想象中大
不同系统对 cron 表达式的支持并不统一:
- 有些系统(比如 Quartz、Spring、AWS EventBridge)会额外加一个秒字段或年份字段,表达式因此变成六个或七个字段。
- 有些支持 MON、JAN 这样的名称,也支持 @daily、@hourly 这类快捷写法,另一些则不支持。
- L、W、# 这些字符(分别表示最后一天、最近的工作日、第几个星期几)只存在于部分方言中。
坑四:夏令时会让任务漏跑或跑两次
在实行夏令时的本地时区里,被跳过的那一个小时中安排的任务可能不会执行;而重复的那一个小时里,任务可能跑两次。把调度时间设在 UTC,可以完全绕开这个问题。
上线前先做一次校验
把表达式粘进一个解析器,读一遍它翻译出来的大白话。如果翻译结果跟你的本意对不上,就在它悄悄跑错时间之前改掉。
几个高频问题也顺带说清楚:*/5 * * * *就是每 5 分钟跑一次,分钟字段的 */5 是步长 5,其余字段是通配符;每天午夜跑一次用0 0 * * *,即第 0 分钟、第 0 小时、每天、每月、每周的每一天;cron 用的是运行它的机器或调度服务的时区,很多服务器和云调度器默认 UTC,重要的时候要显式设置;五字段和六字段的区别在于,标准 Unix cron 是五个字段,从分钟到星期几,而有些调度器会在前面加秒字段、或在后面加年份字段,用之前先确认工具要的是哪种格式。
热门跟贴