linux Shell 定时任务重复执行的本质是多个实例 并发 导致资源争用或逻辑错误,应根据任务特性选择防并发、保幂等或控节奏策略:用 flock 实现脚本级互斥、进程名检测轻量防重、设计幂等任务、调整调度节奏与超时机制。

Linux Shell 定时任务重复执行,本质是多个相同任务实例同时运行导致资源争用或逻辑错误。避免冲突的关键不是单纯加锁,而是根据任务特性选择合适策略:防止并发、确保幂等、或控制执行节奏。
使用 flock 实现脚本级互斥执行
flock 是最常用且轻量的文件锁方案,适合单机环境下的简单防重。它通过在脚本开头对某个固定文件加锁,确保同一时刻仅一个实例运行。
- 在 crontab 中调用脚本时,直接包裹 flock 命令:
* * * * * flock -n /tmp/myjob.lock -c ‘/path/to/your/script.sh’ || echo “Job skipped: already running” - -n 表示非阻塞,若锁已被占用则立即退出,避免任务 堆积
- 锁文件路径建议用绝对路径,且确保所有实例指向同一文件(如 /tmp/ 下需注意 tmpfs 清理风险)
- 不推荐在脚本内部调用 flock,容易因异常退出导致锁残留;应由 cron 直接控制
基于进程名检测的轻量级防重
适用于无法依赖外部文件系统(如容器只读层)、或需快速判断是否已有活跃进程的场景。
- 在脚本开头加入检查逻辑:
if pgrep -f “script.sh” | grep -v $$ > /dev/NULL; then exit 1; fi - 注意过滤当前 shell 进程 PID($$),否则每次都会误判为已存在
- pgrep -f 匹配完整命令行,比 pidof 更可靠,但需确保脚本启动方式具有一致性(如避免带随机参数)
- 该方法无持久锁文件,重启后自动恢复,但存在极短时间窗口的竞态(如两个实例几乎同时通过检测)
任务设计层面的幂等性保障
真正健壮的防重,应让任务本身支持重复执行而不产生副作用。这是 分布式 或高可用场景下的首选思路。
- 操作前先校验状态:例如备份任务先检查目标归档文件是否存在且时间戳最新
- 关键步骤添加唯一标识:如生成带时间戳 + 随机串的临时目录,配合 mv 原子操作完成结果落盘
- 数据库 类任务务必使用 WHERE 条件 + 影响行数判断,避免 INSERT 重复主键或 UPDATE 覆盖有效数据
- 日志中记录执行 ID 和起止时间,便于事后审计是否真有重复发生
调整调度节奏与失败重试策略
很多“重复”其实源于任务超时未退出,而 cron 又触发了下一轮。合理设置执行周期和超时机制可大幅降低冲突概率。
- 用 timeout 命令限制单次最大运行时长:
* * * * * timeout 300 /path/to/script.sh || echo “Job timed out” - 避免使用过于频繁的间隔(如每分钟多次),除非任务秒级完成且必须高频
- 对可能失败的任务,不要依赖 cron 自动重试,应在脚本内实现指数退避或有限重试逻辑
- 结合日志轮转与清理机制,防止旧日志干扰新任务的状态判断