如何防止 cron 任务重复(并发)执行?
任务执行时间超过调度间隔会导致并发重叠。标准解法:flock -n 锁文件——上一个没跑完,新的直接跳过。本文给出用法、脚本内置锁与应用内幂等设计。
标准解法:用 flock——* * * * * /usr/bin/flock -n /tmp/job.lock /path/to/job.sh。它给锁文件加排他锁:上次任务没跑完锁还在,新一轮直接跳过(-n 非阻塞),跑完自动释放。执行时长超过调度间隔的任务都必须加这层保护。
flock 用法
* * * * * /usr/bin/flock -n /tmp/backup.lock /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
-n:拿不到锁立即退出(不加则等待,可能越堆越多)- 锁文件任意路径,建议 /tmp 或 /run(重启自动清)
- 想记录"被跳过":
-n退出码为 1,可包一层记日志
脚本内置锁(自带防重入)
不想依赖 crontab 写法,把锁写进脚本:
#!/bin/bash
exec 200>/tmp/myjob.lock # 打开文件描述符 200
flock -n 200 || { echo "$(date) 上一个实例还在跑,跳过"; exit 1; }
# 正常任务逻辑
好处:怎么调用(cron、手动、其他调度器)都受保护。
其他可选方案
| 方案 | 说明 |
|---|---|
| systemd timer | OnCalendar 触发的 service 默认不并发(上一个没结束不启新实例) |
| 应用内分布式锁 | Redis SETNX/数据库锁,多机部署时必须用这个,flock 只管单机 |
| PID 文件自检 | 脚本开头查 PID 文件有效性,老派写法 |
多机场景的特别提醒
flock 只防单机并发。同一个任务在多台的 crontab 里都配了(高可用部署常见),要用 Redis/数据库级的分布式锁,或让任务只在 leader 节点跑;更省心的是把这类任务收敛到一台/一个 K8s CronJob。
观测云对照
并发重叠在数据里看得见。 任务执行时长超过间隔的趋势(观测云里看任务日志的开始/结束时间差)是加锁需求的早期信号;被 flock 跳过的次数也可打点统计,长期跳过多说明该优化任务耗时了。
常见问题(FAQ)
Q:flock 的锁文件要定期清理吗?
A:不用。锁是加在打开的文件句柄上的,文件本身留着无妨;放 /tmp 或 /run 重启自清。
Q:任务卡死了锁永远不释放?
A:进程退出(包括被 kill)锁自动释放;flock 不会死锁残留。但任务本身卡死要另外加超时(timeout 300 job.sh)。
Q:想"排队执行"而不是"跳过"?
A:去掉 -n,新实例会等锁释放再跑;注意大量堆积的风险,一般跳过+告警更合理。