用 flock 防止 crontab 任务重叠执行
用 flock 防止 crontab 任务重叠执行
Linux 服务器上最常见的定时任务写法,是把脚本丢进 crontab。任务本身通常没问题,真正容易踩坑的是:上一轮还没跑完,下一轮又被 cron 拉起来了。备份、报表、同步这类工作一旦重叠,轻则重复写入、日志交错,重则把同一份数据改乱,甚至把磁盘或数据库打满。
flock 是 util-linux 自带的文件锁工具,大部分发行版默认就有,不必再装一套任务框架。它的思路很简单:同一把锁同一时刻只允许一个进程持有。任务开始时先抢锁,抢到就跑;抢不到就说明上一轮还在,直接退出或等待。
先确认系统里有 flock
flock --version
若提示找不到命令,Debian / Ubuntu 可以安装 util-linux,RHEL 系一般已经自带。
最常用的写法
假设脚本是 /usr/local/bin/backup.sh,每天凌晨执行,平时几分钟能结束,但数据变多时可能超过 1 小时。可以这样写 crontab:
5 2 * * * flock -n /var/lock/backup.lock /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
几个要点:
-n表示非阻塞。锁被占用时立即失败退出,避免第二天的任务在队列里干等,把多个实例越积越多。- 锁文件路径要固定,并且保证运行用户对该目录可写。
/var/lock或/run/lock是常见选择。 flock退出码非 0 时,多半是没抢到锁,而不是脚本本身失败。看日志时要先分清这两种情况。
如果希望后来的任务等到上一轮结束再跑,去掉 -n 即可。备份、对账这类「必须补跑」的任务可以用等待;日志清理、心跳探测则更适合 -n,错过一轮通常没有关系。
也可以把锁写进脚本内部,方便本地手动执行时同样生效:
#!/bin/bash
exec 9>/var/lock/backup.lock
if ! flock -n 9; then
echo "another backup is still running, exit"
exit 0
fi
# 真正的备份逻辑放在后面
exec 9> 打开一个固定的文件描述符,锁会一直保持到脚本退出(包括异常退出)。不必自己再写解锁,避免漏关锁。
选择锁文件时注意什么
锁文件只是一个占位,不必往里面写内容。不要把锁文件放到会定期被清空的临时目录后就不管了——如果系统清理了锁文件,而旧进程还在,新进程可能再成功加锁,重叠保护就失效了。更稳妥的做法是把锁放在 /var/lock 或应用自己的数据目录里。
同一类任务要用同一把锁;不同任务不要共用一个锁文件,否则会无谓地互相阻塞。反过来,如果两个脚本会改同一份数据,就应该共用一把锁。
和 timeout、日志一起用
任务重叠有时是因为脚本卡住,而不是它真的需要跑那么久。可以给命令加上超时:
flock -n /var/lock/backup.lock timeout 3600 /usr/local/bin/backup.sh
一小时还没结束就强制退出,避免一把锁被挂死的进程长期占着。再配合日志轮转,排障时就能看清是「没抢到锁跳过」,还是「跑到一半被超时杀掉」。
flock 解决不了脚本写得不对的问题,但它能用很小的代价,把 crontab 最常见的重叠执行挡在门外。对于任何可能超过调度间隔的定时任务,建议默认都加上这把锁。
- 点赞
- 收藏
- 关注作者
评论(0)