WSの小屋

Cron 调度表达式:从入门到精通

一、什么是 Cron?

Cron 是 Unix/Linux 系统中的一个经典任务调度工具,用于在指定的时间点定期执行脚本或命令。它的名字来源于希腊语 Chronos(χρόνος),意为"时间"。

Cron 最早出现在 Unix V7(1979 年),由 AT&T 贝尔实验室开发。近半个世纪以来,它已成为任务调度的工业标准——几乎所有操作系统(Linux、macOS、BSD)和云服务(AWS Lambda、Google Cloud Scheduler、Azure Functions、Vercel Cron Jobs)都支持或兼容 Cron 语法。

二、Cron 表达式格式

2.1 标准格式(5 位)

* * * * *
┬ ┬ ┬ ┬ ┬
│ │ │ │ └── 星期 (0-7, 0和7都表示周日)
│ │ │ └──── 月份 (1-12)
│ │ └────── 日期 (1-31)
│ └──────── 小时 (0-23)
└────────── 分钟 (0-59)

每个字段支持以下特殊字符:

字符 含义 示例
* 任意值(每个单位都匹配) * 每分钟、每小时...
, 枚举多个值 1,15 第 1 和第 15
- 范围 1-5 1 到 5
/ 步长 */5 每 5 个单位

⚠️ 注意:Cron 的时间都是基于系统时区的,通常默认 UTC。

2.2 带秒的格式(6 位)

某些实现(如 Quartz、Spring、阿里云 OSS)支持 6 位表达式,在标准格式前加上

0 * * * * *
┬ ┬ ┬ ┬ ┬ ┬
│ │ │ │ │ └── 星期 (0-7)
│ │ │ │ └──── 月份 (1-12)
│ │ │ └────── 日期 (1-31)
│ │ └──────── 小时 (0-23)
│ └────────── 分钟 (0-59)
└──────────── 秒 (0-59)

三、常见示例详解

3.1 基础示例

表达式 含义
0 0 * * * 每小时整点执行
*/5 * * * * 每 5 分钟执行一次
0 */2 * * * 每 2 小时执行一次
0 8 * * 1-5 工作日每天早上 8 点
30 18 * * 5 每周五下午 6:30
0 0 1 * * 每月 1 号凌晨执行
0 0 1 1 * 每年 1 月 1 日执行
0 */30 9-18 * * 每天 9:00~18:00 每 30 分钟执行

3.2 进阶示例

表达式 含义
0 0,12 * * * 每天 0 点和 12 点执行
*/10 8-17 * * 1-5 工作日上午 8 点到下午 5 点,每 10 分钟执行
0 2 * * 0 每周日凌晨 2 点
0 0 1 */3 * 每 3 个月的第 1 天执行
15 10 * * * 每天 10:15 执行
0 3 * * 6 每周六凌晨 3 点
0 0 31 * * 每月 31 号(没有 31 号的月份不会执行)

四、常见 Cron 实现与差异

不同的 Cron 实现在语法上存在一些差异,值得注意:

特性 Linux Cron Quartz Spring @Scheduled Node.js node-cron
字段数 5 6-7 5-6 6
秒支持
L / W / #
年字段
别名 ✅(如 @daily ✅(@Scheduled(cron="...")

在 Linux 中还有这些方便的时间别名:

别名 等价表达式 含义
@reboot N/A 系统启动时
@yearly 0 0 1 1 * 每年
@annually 0 0 1 1 * 每年
@monthly 0 0 1 * * 每月
@weekly 0 0 * * 0 每周
@daily 0 0 * * * 每天
@hourly 0 * * * * 每小时

五、Cron 调度详解

5.1 分钟:0-59

# 每分钟执行
* * * * * command

# 每 5 分钟
*/5 * * * * command

# 在 0、15、30、45 分钟时执行
0,15,30,45 * * * * command

# 在 10~20 分钟之间每分钟执行
10-20 * * * * command

5.2 小时:0-23

# 每小时执行
0 * * * * command

# 每天 0 点
0 0 * * * command

# 每天 9 点到 18 点,每 2 小时执行(即 9、11、13、15、17 点)
0 */2 9-18 * * command

# 每天 0、8、16 点执行
0 0,8,16 * * * command

5.3 日期:1-31

# 每月 1 号和 15 号
0 0 1,15 * * command

# 每月最后一天(需要手动计算或使用工具)
0 0 28,29,30,31 * * [ "$(date +\%m -d tomorrow)" != "$(date +\%m)" ] && command

# 每 10 天执行一次
0 0 */10 * * command

5.4 月份:1-12

# 每季度初(1月、4月、7月、10月)
0 0 1 1,4,7,10 * command

# 每年 6 月到 9 月之间的工作日早上 9 点
0 9 * 6-9 1-5 command

# 半年一次(1 月和 7 月)
0 0 1 1,7 * command

5.5 星期:0-7(0 和 7 都表示周日)

# 工作日(周一到周五)早上 8 点
0 8 * * 1-5 command

# 周末(周六周日)
0 8 * * 0,6 command

# 每周日到周四,每小时的 30 分执行
30 * * * 0-4 command

# 仅在周三执行
0 0 * * 3 command

六、常见陷阱与最佳实践

6.1 五个关键陷阱

陷阱 错误示例 说明
日期与星期同时使用 0 0 1 * 1 每月 1 号且是周一才执行,很少是你想要的
步长起始 */2 * * * * 在 0、2、4...分钟执行,不是"从第一条记录开始"
时区问题 0 8 * * * 如果服务器 UTC 8 点 = 用户本地可能 16 点
DST 调整 0 3 * * * 夏令时切换时 3 点可能跳过一次或执行两次
秒级精度 * * * * * script 非秒级精度,平均每 60 秒执行,不会精确到秒

6.2 最佳实践

1. 日期与星期避免同时使用

# ❌ 容易误解
0 0 15 * 5    # 每月 15 号且是周五才执行

# ✅ 用两个独立任务
0 0 15 * * command  # 每月 15 号
0 0 * * 5 command   # 每周五

2. 使用随机延迟避免"惊群效应"

# ❌ 整点每分钟执行,太多任务同时触发
0 * * * * curl https://api.example.com/sync

# ✅ 在 0~9 分钟间随机延迟
# 在脚本内加入:sleep $((RANDOM % 600))
0 * * * * bash -c 'sleep $((RANDOM % 600)); curl https://api.example.com/sync'

3. 日志与输出重定向

# ❌ 丢失输出
*/5 * * * * /usr/bin/backup.sh

# ✅ 记录日志
*/5 * * * * /usr/bin/backup.sh >> /var/log/backup.log 2>&1

# ✅ 使用 logger 写入 syslog
*/5 * * * * /usr/bin/backup.sh | logger -t backup

4. 不要在脚本中使用相对路径

# ❌ 可能找不到命令
0 3 * * * mysqldump -u root test > backup.sql

# ✅ 使用绝对路径
0 3 * * * /usr/bin/mysqldump -u root test > /backup/$(date +\%Y\%m\%d).sql

# ✅ 或先 cd 到目标目录
0 3 * * * cd /backup && /usr/bin/mysqldump -u root test > backup.sql

5. 用文本注释说明含义

# ┌────────── 每周日凌晨 3:00 全量备份
# │ 由于全量备份耗时约 30 分钟,安排在非高峰时段
# │ 日志: /var/log/backup-full.log
# ▼
0 3 * * 0 /usr/bin/backup.sh --full

七、在线工具与调试

工具 地址 用途
Crontab Guru https://crontab.guru 最常用的在线 Cron 解析
Cron Expression Generator https://www.freeformatter.com/cron-expression-generator-quartz.html Quartz 格式生成器
中文 Crontab https://www.bejson.com/othertools/cron/ 中文界面,方便快速验证

快速调试命令:

# 查看当前用户的 crontab
crontab -l

# 编辑 crontab
crontab -e

# 查看 cron 日志(Ubuntu/Debian)
grep CRON /var/log/syslog

# 查看 cron 日志(RHEL/CentOS)
tail -f /var/log/cron

八、各语言/平台的 Cron 用法

Shell / Linux Crontab

# 编辑当前用户定时任务
crontab -e

# 格式
0 2 * * * /path/to/script.sh

Node.js (node-cron)

const cron = require('node-cron')

cron.schedule('*/5 * * * *', () => {
  console.log('每 5 分钟执行')
})

Python (croniter)

import croniter
import datetime

# 解析 cron 表达式
cron = croniter.croniter('*/5 * * * *', datetime.datetime.now())
next_time = cron.get_next(datetime.datetime)
print(f"下次执行: {next_time}")

GitHub Actions

on:
  schedule:
    # 每天早上 8 点(UTC)
    - cron: '0 8 * * *'

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - run: echo "定时任务执行"

Vercel Cron Jobs

{
  "crons": [
    {
      "path": "/api/sync",
      "schedule": "0 */3 * * *"
    }
  ]
}

总结

要点 说明
Cron 是什么 基于时间的任务调度器,5 个字段描述执行周期
核心字段 分钟 小时 日期 月份 星期
特殊字符 * 任意、, 枚举、- 范围、/ 步长
最大陷阱 日期+星期同时使用、时区问题、DST 切换
最常用表达式 * * * * * / */5 * * * * / 0 0 * * *

掌握 Cron 表达式的写法,是运维和云原生开发的基本功。"先理解时间逻辑,再写表达式"——用自然语言描述你的需求("每个工作日的上午 9 点到 17 点,每 30 分钟一次"),再翻译成 Cron 表达式,这样基本不会出错。

上一篇

留言板

下一篇

小游戏

Comments | 0条评论