linux cron不处理并发,易致多实例并行;解决核心是互斥执行:可用flock加锁、进程检查、systemd timer替代或业务层幂等设计。

Linux的cron本身不处理并发控制,同一任务若执行时间长于设定周期,就可能触发多个实例并行运行,造成数据错乱、资源争用甚至系统崩溃。解决核心是让任务具备“互斥执行”能力。
用flock加锁确保单实例运行
flock是最轻量且可靠的方案,它基于文件锁机制,在脚本开头对某个锁文件加排他锁,执行完毕自动释放。
- 在crontab中这样写:* * * * * flock -n /tmp/myjob.lock -c '/path/to/script.sh' 2>/dev/null || echo "Job skipped: already running"
- -n 表示非阻塞,加锁失败直接退出,避免任务卡住;-c 后跟要执行的命令(注意用单引号包裹)
- 锁文件路径建议用绝对路径,且确保所有实例指向同一个文件(如/tmp/myjob.lock或/var/run/myjob.lock)
检查进程是否存在(慎用)
通过pgrep或pidof判断同名进程是否已在运行,适合简单场景,但有竞态风险(检查和启动之间可能被抢占)。
- 示例脚本片段:if ! pgrep -f "script.sh" > /dev/null; then /path/to/script.sh &; fi
- 务必用-f匹配完整命令行,并确保脚本名或关键字足够唯一,避免误杀其他进程
- 不推荐用于关键任务,仅作临时或低频辅助手段
用systemd timer替代cron(长期推荐)
systemd原生支持StartLimitIntervalSec和StartLimitBurst,可限制单位时间内最大启动次数;更关键的是,配合Type=oneshot与RemainAfterExit=yes,天然规避并发。
- 定义service文件(如/etc/systemd/system/myjob.service),设置[Service] Type=oneshot
- 定义timer文件(如myjob.timer),启用Persistent=true可补漏未执行的周期
- 启用后,systemd会确保前一次执行结束才触发下一次,无需额外加锁逻辑
业务层主动规避(治本之策)
最稳妥的方式是从任务设计入手,让任务本身具备幂等性与状态感知能力。
- 在脚本开头写入时间戳到状态文件,执行结束时更新或删除;下次启动先读取该文件,若距上次开始不足阈值,则跳过
- 操作数据库时加业务唯一约束(如用INSERT ... ON CONFLICT DO NOTHING),或用分布式锁(Redis SETNX)协调跨机器任务
- 对定时清理类任务,改用“滑动窗口”:每次只处理now() - interval '10 minutes'之后的数据,而非固定时间点










