Linux 定时单元如何选择 OnCalendar 与 Persistent:错过窗口后的补跑取舍
来源:17golang原创
时间:2026-08-27 14:47:26 375浏览 收藏
夜间归档任务最容易出现一种“看起来没报错,实际少跑一次”的问题:服务器在计划时间关机或服务被暂停,第二天开机后任务到底要不要补跑?在 Linux 的 systemd timer 中,OnCalendar 负责描述日历触发点,Persistent=true 负责记录并处理错过的触发窗口;两者不是同一个开关。
需要补跑离线期间错过的日历任务,就在对应的 timer 单元里使用
Persistent=true;只想等待下一次计划时间,则保持默认的非持久行为。先把 service 单元和 timer 单元分开验证,再判断是否真的发生了补跑。
要点速览
OnCalendar只描述何时触发,不能单独表达“错过后补跑”。Persistent=true让 timer 记录上次触发时间,并在重新激活后检查是否错过。- 服务单元负责一次任务的启动命令,timer 单元负责日历调度,二者要用同名关系连接。
- 用
systemctl list-timers、systemctl status和 journal 日志核对结果,不要只看配置文件。
先把一次任务和日历调度拆成两个单元
假设每天凌晨 2 点压缩 /srv/archive。一次归档动作应该放在 archive-daily.service,时间规则放在 archive-daily.timer。这种拆分让“手动跑一次”和“按日历自动跑”共用同一份任务定义,也方便分别检查失败点。
[Unit]
Description=Daily archive service
[Service]
Type=oneshot
ExecStart=/usr/local/bin/archive-daily
[Unit]
Description=Daily archive timer
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
Unit=archive-daily.service
[Install]
WantedBy=timers.target
把文件放入 /etc/systemd/system/ 后,先执行 systemctl daemon-reload,再用 systemctl enable --now archive-daily.timer 激活定时单元。这里的关键不是记住一长串命令,而是确认 timer 的 Unit 指向了真正执行一次归档的 service。

图:日历触发只经过 timer unit,实际动作由 service unit 的启动命令完成。
OnCalendar 与 Persistent=true 分别解决什么
OnCalendar=*-*-* 02:00:00 表示每天的日历触发时间。若机器一直在线,timer 会在这个时刻激活 service;若机器在 01:50 到 03:10 之间关机,单靠 OnCalendar 并不会自动把“错过的那次”变成一次执行。
启用 Persistent=true 后,timer 会把上次触发时间写入持久化状态。当它重新激活时,如果发现日历触发点已经过去,就会补激活关联的 service。补跑的判断来自上次触发时间与当前日历的差距,不是把离线期间的每个小时逐个补齐。

图:OnCalendar 给出计划点,Persistent=true 让重新激活时能识别错过窗口并触发一次补跑。
用三个检查点确认补跑是否真的发生
配置写对不等于任务已经按预期运行。可以按下面顺序检查,避免看到“active”就误以为归档成功。
- 看下一次计划:执行
systemctl list-timers archive-daily.timer,确认列出的 NEXT 时间符合本机时区和目标日期。 - 看 timer 状态:执行
systemctl status archive-daily.timer,关注Trigger、最近一次激活时间以及 timer 是否仍是 active。 - 看 service 结果:执行
systemctl status archive-daily.service和journalctl -u archive-daily.service -n 30,确认补跑对应的 service 退出为成功,并且归档目录出现新的文件。
如果只看到 timer 已激活,却没有 service 的新日志,先检查 timer 文件里的 Unit=archive-daily.service 是否拼写一致;如果 service 日志显示启动命令本身失败,修复任务脚本后再单独手动启动 service 验收。
什么时候不该启用 Persistent=true
补跑不是越积极越好。清理临时文件、生成日报这类“错过一次也不影响后续”的任务通常适合补跑;而发送一次性通知、扣库存、生成不可重复的外部账单,就要先确认任务是否幂等。若补跑会重复产生副作用,可以不启用 Persistent=true,或让 service 自己用日期文件、业务流水号做去重。
还要注意 Persistent=true 不会替你补齐多次历史触发,也不会保证任务执行成功。它解决的是“timer 被重新激活时,是否发现曾经错过日历点”这一层问题;任务本身的重试、锁定和结果告警仍应放在服务逻辑里。
相关问题
OnCalendar 可以写成每隔几分钟吗?
可以使用 systemd 日历表达式描述周期,但需要先确认它表达的是墙上时间还是固定间隔。若任务更适合“上一次完成后再等待一段时间”,应评估单调计时器语义,而不是强行套日历规则。
修改 timer 文件后为什么没有变化?
修改单元文件后先执行 systemctl daemon-reload,必要时再重启或重新激活 timer。用 systemctl cat archive-daily.timer 检查当前加载的内容,避免只改了备份目录里的文件。
补跑后如何避免重复处理归档文件?
让归档脚本以目标日期或输入文件摘要生成幂等键,写入成功标记后再提交结果。timer 只负责触发,不能替代业务侧的去重设计。
把选择落到一条判断线上
先问任务是否允许在重新开机后补跑:允许,就在 timer 单元加入 Persistent=true;不允许,就让它等待下一次 OnCalendar。无论选哪种,最后都要用 timer 状态、service 状态和 journal 日志做一次闭环核对。这样排查时能明确是没有触发、没有关联到 service,还是 service 自己执行失败。
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习