防止crontab任务并发冲突的核心是让任务“天然互斥”,推荐flock文件锁(-n非阻塞、绝对路径锁文件)、systemd timer(persistent=true、资源限制)及脚本幂等设计(唯一标识、原子操作、状态校验),辅以错峰调度与超时控制。

防止 Crontab 任务并发冲突,核心不是调准时间,而是让任务“天然互斥”——哪怕执行超时、系统重启、手动触发,也绝不会同时跑两个实例。
flock 文件锁:最简可靠的防并发手段
在 crontab 调度层直接加锁,不改脚本,上线快、风险低。它依赖内核级文件锁,保证同一时刻最多一个进程持有锁。
- 必须用 -n(非阻塞) 参数:抢不到锁立刻退出,不排队、不堆积,避免后续任务全部卡住
- 锁文件路径要用绝对路径,推荐 /var/run/xxx.lock(比 /tmp/ 更稳定,不易被系统清理)
- 所有同类任务共用同一个锁文件,例如数据库备份、日志归档、配置同步等应各自独立锁路径
- 写法示例:
0 2 * * * flock -n /var/run/backup.lock -c '/opt/scripts/backup-db.sh' || echo "$(date): skipped" >> /var/log/backup.log
systemd timer:适合关键任务的长期方案
当任务涉及生产数据库、需跨系统重启保障或要求资源可控时,systemd timer 比 crontab 更健壮,从设计上杜绝并发。
- 定义
myjob.service:Type=oneshot,RemainAfterExit=yes - 配
myjob.timer:OnCalendar=02:00,Persistent=true(补漏未执行周期) - systemd 天然保证:前一次未结束,下一次绝不会触发
- 可直接限制内存(MemoryMax=512M)、超时(RuntimeMaxSec=1800)、失败重试策略
- 状态一目了然:
systemctl status myjob.timer查下次执行时间,journalctl -u myjob.service看详细日志
脚本自身要幂等:多跑一次也不能出错
即使调度层已加锁,脚本仍需具备容错能力——这是异常恢复或人工触发时的底线。
- 每次生成唯一标识,如
$(date +%Y%m%d_%H%M%S)_$$,用于临时文件、日志路径、归档名,避免覆盖 - 导出前检查目标文件是否存在且距今不足 24 小时,直接跳过(与 cron 周期配合兜底)
- 数据库导出用
mysqldump --single-transaction保障一致性;落盘用mv tmp.sql final.sql原子操作 - 关键步骤写入状态文件(如
/var/lib/myjob/status),含开始/结束时间、成功标记,供下次启动校验
错峰 + 超时:降低冲突发生概率
再强的锁机制,也挡不住大量长耗时任务在同一秒启动造成的 I/O 或 CPU 尖峰。主动错开是成本最低的减压方式。
- 同类备份任务不要全设在
0 2 * * *,可分别设为7 2 * * *、15 2 * * *、22 2 * * * - 高频任务(如每分钟)避免全卡在 :00 秒,改为
0,15,30,45 * * * *或开头加sleep $((RANDOM % 30)) - 在宝塔面板的任务备注里注明“耗时约 8 分钟,内存峰值 1.2G”,方便协同调整资源窗口
- 对超长任务设置合理超时,比如
timeout 3600 /opt/scripts/backup.sh,防止单次失控拖垮整条链路











