systemd timer 的 Persistent 为什么能补跑错过任务
一台服务器每天 02:30 生成报表,但它在 02:00 关机维护,04:00 才重新启动。普通日历定时器会继续等待下一个 02:30;加入 Persistent=true 后,systemd 会在 timer 再次激活时检查持久化的上次触发时间。如果 OnCalendar= 在未激活期间至少应该到点一次,就补触发一次对应服务。
Persistent 的本质不是保存“待执行任务队列”,而是保存上次触发时间,并在 timer 激活时完成一次是否错过日历时刻的判断。停机期间错过 1 次或 10 次,恢复后通常都只产生一次 service 激活。
这个边界很重要:它只对带 OnCalendar= 的 timer 生效,不会让 OnBootSec=、OnUnitActiveSec= 等单调时钟配置自动获得同样的补跑语义。下面用一个每日同步任务把配置、激活和检查动作串起来。
补跑判断的核心关系
根据 systemd 当前的 timer 文档,启用 Persistent= 后,服务最后一次被 timer 触发的时间会保存到磁盘。timer unit 后来再次激活时,systemd 会把这个时间与当前时间及 OnCalendar= 计划比较;只要未激活期间至少存在一个应触发时刻,就触发服务。这个判断发生在 timer 激活时,所以只写配置但没有启动 timer,并不会凭空执行任务。

还要分清“关机”和“睡眠”。对于 OnCalendar=,机器挂起期间实时时钟不会暂停;恢复时,睡眠期间到点的日历 timer 会被处理。如果连续睡眠期间到点多次,同样只会形成一次服务激活。Persistent=true 主要补上的是 timer 未激活或系统关机导致的缺口。
所谓“立即补跑”也有一个配置层面的例外:如果 timer 设置了 RandomizedDelaySec=,补触发仍会受到随机延迟约束。因此判断现场时,不要只看开机后的几秒钟;应同时查看 timer 的 NEXT、LAST 和该随机延迟配置。
把计划、执行与观察拆成三个边界
systemd 推荐把任务命令放进 .service,把触发计划放进同名 .timer。例如 report-sync.timer 默认会激活 report-sync.service。这样做的好处是:计划是否到点、命令是否成功、补跑是否发生,分别有清晰的检查入口。

先定义只负责执行任务的 service
创建 /etc/systemd/system/report-sync.service。示例中的命令只是占位,实际使用时应替换成自己的同步脚本,并保证脚本重复执行不会制造重复数据。
[Unit] # 说明这个一次性任务的用途 Description=Synchronize daily report data [Service] # 每次触发执行一次,命令结束后 service 退出 Type=oneshot # 替换成实际任务;脚本应能安全地重复执行 ExecStart=/usr/local/bin/report-sync
补跑通常发生在系统刚恢复、网络尚未稳定或外部依赖仍在启动的阶段。如果任务强依赖网络,可以在 service 的 [Unit] 中按实际环境增加对网络就绪目标的依赖;不要仅凭 timer 已触发就假设业务依赖必然可用。
再定义带 Persistent 的日历 timer
创建 /etc/systemd/system/report-sync.timer:
[Unit] # timer 只负责计划,不直接承载业务命令 Description=Run report synchronization every day [Timer] # 每天本地时间 02:30 形成一个日历触发点 OnCalendar=*-*-* 02:30:00 # timer 重新激活时检查未激活期间是否错过日历触发点 Persistent=true # 允许 systemd 在一分钟窗口内合并唤醒 AccuracySec=1min [Install] # 开机进入 timers.target 时加载这个 timer WantedBy=timers.target
AccuracySec=1min 表示允许一定精度窗口,它不是失败重试,也不是随机延迟。若业务要求更精确,可以调小;若大量主机使用相同计划并希望错峰,应另外评估 RandomizedDelaySec=,同时接受补跑可能被延后的结果。
启用之前先核对日历表达式
systemd-analyze calendar 能解析日历表达式并列出下一次触发时间,适合在加载 unit 前发现时区、星期或日期写法错误。
# 解析表达式并显示规范形式及下一次触发时间 systemd-analyze calendar '*-*-* 02:30:00'
确认表达式后重新加载 unit,启用并立即启动 timer:
# 让 systemd 重新读取新增或修改过的 unit 文件 sudo systemctl daemon-reload # 建立开机启用关系,并在当前系统立即激活 timer sudo systemctl enable --now report-sync.timer
enable 负责下次启动时随 timers.target 激活,--now 负责当前这一次立即启动。只执行 enable 而不启动时,本次运行周期里 timer 可能仍未激活,也就不会立即进行 Persistent 的补跑判断。
用三个检查点确认计划和补跑
检查 timer 是否真的在等待
# 查看 NEXT、LEFT、LAST、PASSED、UNIT 和 ACTIVATES systemctl list-timers --all report-sync.timer # 查看加载状态、启用状态和下一次触发信息 systemctl status report-sync.timer
NEXT 是下一次计划时间,LAST 是最近一次触发时间,ACTIVATES 应指向 report-sync.service。如果列表中完全没有该 timer,先检查 unit 是否加载成功以及是否已启动,而不是直接怀疑 Persistent。
检查 service 是否被补触发
# 查看本次开机后 service 的触发与退出记录 journalctl -b -u report-sync.service # 连同上一次开机记录检查关机前后的触发情况 journalctl -b -1 -u report-sync.service
判断标准不是“日志里出现 Persistent 字样”,而是:关机前最后触发时间早于一个或多个计划点,timer 在恢复后重新激活,并且 service 随后出现一次新的启动记录。若设置了随机延迟,应把延迟窗口纳入观察。
需要受控验证时怎样操作
在测试机上可以临时选择较短但仍是 OnCalendar= 的计划,先让 timer 正常触发一次,再停止 timer,使一个计划点在停止期间经过,最后重新启动 timer。若 Persistent=true 生效,重启 timer 后应看到 service 新增一次启动记录。不要在生产机用修改系统时间的方式测试,它会干扰日志顺序、证书和其他日历任务。
# 暂停 timer,让一个 OnCalendar 计划点在未激活期间经过 sudo systemctl stop report-sync.timer # 经过计划点后重新激活;此时会执行 Persistent 判断 sudo systemctl start report-sync.timer # 观察 service 是否只新增了一次启动记录 journalctl -u report-sync.service --since '15 minutes ago'
最容易误判的五个地方
误以为每个错过时刻都会逐个回放
Persistent 判断的是“未激活期间是否至少错过一次”。每天执行一次的任务若停机三天,恢复后不是连续执行三遍,而是补一次。若业务必须逐日补齐,service 自己需要根据业务水位、日期分区或游标计算缺口;timer 不能替代业务级补偿队列。
把单调时钟 timer 当成日历补跑
Persistent= 只对 OnCalendar= 有效。OnBootSec= 与 OnStartupSec= 在激活时若目标时刻已经过去,本身有立即到期规则,但这不是 Persistent 保存历史日历触发点的机制。OnUnitActiveSec= 则表示相对上次激活的间隔,也不能用 Persistent 把停机期间的多个间隔逐个还原。
只启用 service,没有启用 timer
应启用 report-sync.timer,而不是把一次性 report-sync.service 设为开机常驻。timer 激活后才会维护计划、持久时间和下一次触发信息。
服务已经运行时期待它被重复启动
timer 到点只会请求激活目标 unit。如果目标 service 当时已经处于 active 状态,systemd 不会因为 timer 再到点就自动重启它。长任务应结合运行时长、并发策略和任务锁设计,不能把 Persistent 当成并发执行开关。
忽略时间同步与时区
OnCalendar= 使用实时时钟。系统时间、时区或 RTC 不正确,会直接影响日历判断。systemd 会为带 OnCalendar 的 timer 添加与时间设置、时间同步目标相关的排序关系,但主机本身仍应有可靠的时间同步配置。
配置与判断速查表
| 需求或现象 | 关键配置/检查 | 实际语义 |
|---|---|---|
| 关机期间错过日历任务 | OnCalendar= + Persistent=true | timer 激活时补触发一次 |
| 停机期间错过多个计划点 | 检查业务是否自行补齐日期 | systemd 不逐条回放,只激活一次 service |
| 开机后没有立刻补跑 | 检查 timer active 状态和 RandomizedDelaySec= | 补触发可能受随机延迟约束 |
| 确认表达式 | systemd-analyze calendar | 解析计划并显示下一次触发时间 |
| 确认 timer 计划 | systemctl list-timers --all | 查看 NEXT、LAST 与 ACTIVATES |
| 确认任务执行 | journalctl -u report-sync.service | 查看 service 的真实启动与退出记录 |
| 清除持久时间状态 | 停止 timer 后使用 systemctl clean --what=state | 删除 Persistent 维护的时间戳状态 |
结论
Persistent=true 能补跑,是因为 systemd 把上次服务触发时间保存到磁盘,并在 timer 重新激活时拿它与 OnCalendar= 计划做比较。它解决的是“至少错过一次后补一次”,不是历史任务逐条回放。把 timer 正确启用、让业务命令保持幂等,再用 list-timers、status 和 journalctl 分别核对计划、状态与执行记录,才能判断补跑是否按预期发生。
相关问题
Persistent=true 会在第一次创建 timer 时补跑吗?
不要把“首次创建”理解为天然存在历史欠账。补跑判断依赖 timer 激活时可用的持久状态和日历计划;新部署时应以实际的 LAST、状态文件和 service 日志为准,不要假设部署前的所有日历时刻都会被回放。
服务器睡眠期间也需要 Persistent 吗?
OnCalendar 使用实时时钟,系统从睡眠恢复后会处理睡眠期间已经到点的日历 timer;多次到点仍只形成一次服务激活。Persistent 的核心价值是覆盖 timer 未激活和系统关机的情况。
怎样彻底移除 Persistent 留下的状态?
先停止 timer,再按官方文档使用 systemctl clean --what=state report-sync.timer 清理该选项维护的时间戳文件。卸载 timer unit 前做这一步,可以避免遗留状态影响之后同名 unit。
参考资料:systemd.timer 官方源码文档、systemd.time 官方源码文档、systemd-analyze 官方源码文档。
Go select 同时有多个就绪分支时怎么选择
- 上一篇
- Go select 同时有多个就绪分支时怎么选择
- 下一篇
- 表盘自定义工具遇到问题先看哪里?米坛更新帖与知识库帮助说明
-
- 文章 · linux | 4小时前 | Linux · 故障排查 · Linux GDB coredumpctl systemd-coredump
- coredumpctl 怎么把指定崩溃导出给调试器
- 233浏览 收藏
-
- 文章 · linux | 9小时前 | Linux · 运维 · journalctl journald持久化 systemd日志 Storage persistent 重启日志
- journald 怎么把重启前的日志持久化到磁盘
- 166浏览 收藏
-
- 文章 · linux | 12小时前 | Linux · 故障排查 · 服务管理 · systemd RestartSec StartLimitBurst StartLimitIntervalSec
- systemd 服务频繁重启时怎么设置启动限流
- 426浏览 收藏
-
- 文章 · linux | 18小时前 |
- systemd timer 怎么用 RandomizedDelaySec 错峰执行
- 243浏览 收藏
-
- 文章 · linux | 20小时前 |
- Linux core_pattern 管道处理程序怎么接收崩溃信息
- 132浏览 收藏
-
- 文章 · linux | 23小时前 | Linux io_uring 异步取消 IORING_OP_ASYNC_CANCEL
- Linux io_uring 怎么取消尚未完成的请求
- 395浏览 收藏
-
- 文章 · linux | 1天前 | Linux · Linux user namespace uid_map
- Linux user namespace 的 uid_map 怎么配置
- 231浏览 收藏
-
- 文章 · linux | 1天前 | 网络安全 · Linux 防火墙 nftables 动态集合 set timeout gc-interval
- Linux nftables 动态集合怎么设置元素过期时间
- 470浏览 收藏
-
- 文章 · linux | 1天前 | Linux · 内存管理 · Linux OOM oom_score_adj 进程回收
- Linux oom_score_adj 怎么控制进程被回收优先级
- 306浏览 收藏
-
- 文章 · linux | 1天前 | 进程管理 · Linux cgroup v2 cgroup.freeze cgroup.events cgroup.procs 进程冻结
- Linux cgroup v2 怎么冻结并恢复一组进程
- 467浏览 收藏
-
- 文章 · linux | 1天前 | systemd service ReadWritePaths ProtectSystem 文件系统沙箱 Linux服务加固
- systemd ProtectSystem 与 ReadWritePaths 怎么组合
- 485浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 343次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 403次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 404次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 363次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 185次使用
-
- 新能源汽车充电设施巡检记录的设置方法
- 2026-10-04 422浏览
-
- Golangcron定时器和定时任务的使用场景
- 2023-01-28 208浏览
-
- Go编写定时器与定时任务详解(附第三方库gocron用法)
- 2022-12-26 444浏览
-
- 详解golang执行Linuxshell命令完整场景下的使用方法
- 2023-01-07 426浏览
-
- go程序部署到linux上运行的实现方法
- 2023-01-02 387浏览

