oncalendar 触发延迟主因是 randomizeddelaysec 默认1小时叠加唤醒补偿延迟,禁用需设 randomizeddelaysec=0 并配 accuracysec=1min;persistent=true 可补休眠错失的单次触发,但 weekly 语法实际等价于 mon,thu 且不支持 persistent。

OnCalendar 触发时间比预期晚几小时,是 accuracy 设置没生效?
不是 accuracy 没生效,而是 systemd 默认启用了 RandomizedDelaySec(默认 1 小时),且该延迟会叠加在 OnCalendar 基础上——哪怕你只写了 OnCalendar=hourly,实际触发也可能在整点后 0~3600 秒内随机漂移。更隐蔽的是:如果系统刚启动或从休眠恢复,systemd 还会额外施加 up to 1 小时的“唤醒补偿延迟”,合起来就可能晚 2 小时。
实操建议:
- 确认是否真被随机化影响:
systemctl show <service-name>.timer | grep -E "(Accuracy|Randomized)"</service-name>,检查AccuracySec和RandomizedDelaySec实际值 - 禁用随机延迟:在 timer 单元文件中显式写
RandomizedDelaySec=0(不能留空或注释掉) - 若需高精度(如分钟级同步),必须同时设
AccuracySec=1min,否则 systemd 仍可能把执行窗口放宽到默认 1h - 注意:修改后要
systemctl daemon-reload+systemctl restart <name>.timer</name>,仅 reload 不重启 timer 不会重载RandomizedDelaySec
RandomizedDelaySec=0 之后还是不准?检查系统时钟与 suspend 行为
即使关了随机延迟,真实触发时间仍可能偏移几十秒到几小时——常见于笔记本或虚拟机:系统休眠后唤醒,systemd timer 不会“补发”错过的触发,而是等下一个自然周期;更糟的是,如果 OnCalendar 使用了 *-*-* 02:00:00 这类固定时间,而系统在凌晨 2 点关机/休眠,这次触发就永久丢失。
实操建议:
- 用
systemctl list-timers --all查看下次触发时间(Next column),对比当前系统时间,确认是否真延迟,还是单纯“错过一次” - 避免依赖绝对时间:改用
OnBootSec=1h或OnUnitActiveSec=1h,它们不依赖系统时钟对齐,适合后台轮询类任务 - 若必须用
OnCalendar且机器常休眠,加Persistent=true——它会让 timer 在唤醒后立即补跑一次错过的触发(但仅限最近一次,不会累积) - 检查 NTP 同步状态:
timedatectl status,若System clock synchronized: no,OnCalendar解析会出错(比如把 UTC 当本地时间算)
accuracy=1s 和 accuracy=1min 对资源和触发行为的实际影响
AccuracySec 不是“精度承诺”,而是 systemd 主动等待的“容忍窗口”。设太小(如 1s)会让 systemd 每秒检查一次时钟,增加唤醒频率和 CPU 开销;设太大(如 1h)则可能让本该 02:00 执行的任务拖到 02:59 才运行——尤其当系统负载高、timer 被调度延迟时,实际偏差会接近这个值。
实操建议:
- 普通日志轮转、备份任务,
AccuracySec=1min安全且省电;实时性要求高的监控拉取,可压到10s,但别盲目设1s -
AccuracySec必须配合RandomizedDelaySec=0才有效,否则随机延迟会覆盖你的精度设置 - 注意单位:只接受秒(
30s)、分(2min)、时(1h),不支持ms或小数 - 值越小,systemd 越频繁地从 idle 状态唤醒,对电池设备影响明显——
systemd-analyze blame可查 timer 唤醒次数
OnCalendar=weekly 和 OnCalendar=Mon *-*-* 02:00:00 的行为差异
表面都是“每周一凌晨 2 点”,但 weekly 是语法糖,等价于 Mon,Thu *-*-* 00:00:00(systemd 文档明确写死为周一+周四),而 Mon *-*-* 02:00:00 才是真正只在周一 2 点触发。更关键的是:两者对 Persistent 和错失触发的处理逻辑不同。
实操建议:
- 永远用显式格式(如
Mon *-*-* 02:00:00)替代weekly,避免语义误解 -
Persistent=true对weekly无效——它只补最近一次错过的触发,但weekly每次触发都算“新周期”,补的可能是三天前的周一,而非刚错过的那个 - 跨时区部署时,
OnCalendar默认按系统本地时区解析;若需 UTC,必须写OnCalendar=Mon *-*-* 02:00:00 UTC,否则夏令时切换时可能漏掉或重复执行 - 测试用
systemctl trigger <name>.timer</name>不会模拟OnCalendar逻辑,要用systemctl start <name>.timer</name>手动触发验证
真正难调的不是参数本身,而是 timer 行为隐含的“上下文依赖”:系统是否休眠、NTP 是否同步、时区是否设对、甚至 systemd 版本对 Persistent 的实现差异——这些不会报错,只会让触发时间悄悄漂移几个小时。