Crontab无法实现秒级调度,需通过脚本内判断执行时机与锁文件互斥实现伪秒级防并发;每分钟多次触发,脚本按秒余数决定是否执行并原子加锁,失败则exit 1跳过。

在 crontab 中实现秒级精度的防并发任务,核心不是靠 cron 本身(它最小粒度是分钟),而是用脚本内部逻辑控制执行时机和互斥。通过锁文件 + exit 状态码配合,可有效避免同一任务的多个实例并发运行,尤其适合需要近似秒级触发(如每10秒跑一次)但又必须严格串行的场景。
为什么 Cron 本身不支持秒级且需防重入
cron 的最小调度单位是分钟,无法原生表达 */10 * * * * * 这类秒级表达式。若强行用 * * * * * /path/to/script.sh 并在脚本内 sleep 10 秒循环,会因进程长期驻留、kill 不及时、系统重启丢失状态等问题导致失控。更可靠的做法是:让 cron 每分钟触发多次(例如每10秒一次,共6次/分钟),每次执行都快速判断「现在是否该运行」+「当前无其他实例在跑」。
用锁文件实现原子性互斥
锁文件本质是协作式互斥机制,关键在于创建和清理的可靠性:
- 使用
set -o noclobber+touch或mktemp -u+ln -s实现带原子性的锁获取(推荐后者,兼容性更好) - 锁路径建议含任务名与主机标识,如
/tmp/myjob.lock.$(hostname -s),避免多机共享 NFS 时冲突 - 脚本退出前必须
rm -f锁文件;更要设置 trap 捕获 SIGINT/SIGTERM,防止 Ctrl+C 或 kill 导致锁残留 - 加锁失败时立即
exit 1,让调用方(cron)知道本次跳过
用 Exit 码驱动 cron 的“伪秒级”调度
假设你想每10秒执行一次 backup.sh,可在 crontab 中写:
* * * * * /bin/bash -c 'for i in {0..5}; do /opt/scripts/backup.sh --once && sleep 10; done >/dev/null 2>&1'
其中 --once 表示只做单次检查与执行。脚本内部逻辑为:
- 计算当前秒数对 10 取余(
date +%-S | awk '{print $1 % 10}'),仅当余数为 0 时才进入主逻辑 - 尝试获取锁;失败则
exit 1(表示跳过) - 成功获得锁后,执行业务;完成后清理锁并
exit 0 - 若业务出错,仍要清理锁,并按需
exit 2等区分错误类型,便于日志追踪
增强健壮性的细节建议
锁不是银弹,需防范死锁和误删:
- 给锁文件加时间戳内容(如
echo $(date +%s) > $LOCK),并在加锁前检查是否存在且超时(如 > 120 秒),自动清理陈旧锁 - 避免用
ps | grep判断进程是否存在——竞态高、不可靠;锁文件才是唯一可信状态源 - 所有路径用绝对路径,cron 的 PATH 很窄;必要时在脚本开头显式设置
PATH=/usr/local/bin:/usr/bin:/bin - 日志记录每次是否触发、是否抢到锁、耗时多少,方便排查“看似没跑”是被跳过还是被阻塞

















