登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  linux

systemd timer 怎么用 RandomizedDelaySec 错峰执行

来源:17golang原创

时间:2026-10-05 05:24:48 243浏览 收藏

RandomizedDelaySec= 的作用,是在 systemd timer 算出的基准触发时间之后,再增加一段均匀分布的随机延迟。比如每天 02:00 运行的任务配置 RandomizedDelaySec=30min,实际触发会落在 02:00 到 02:30 之间,从而减少多台机器同时访问数据库、对象存储或更新服务器造成的瞬时压力。

官方文档:https://www.freedesktop.org/software/systemd/man/latest/systemd.timer.html

配置要点
  • OnCalendar= 或其他定时条件负责确定基准时刻,RandomizedDelaySec= 只在其上增加随机延迟。
  • 随机值默认在每次触发前重新选择;需要每台机器保持固定偏移时,再启用 FixedRandomDelay=true。
  • AccuracySec= 允许 systemd 合并附近的定时事件,它与随机延迟解决的是不同问题。

前置条件:先确认版本与任务边界

RandomizedDelaySec= 从 systemd 229 起可用,FixedRandomDelay= 从 systemd 247 起可用。先查看系统版本和 timer 手册,避免在较旧发行版上直接套用后文的扩展配置。

# 查看当前 systemd 版本,并确认 timer 手册包含目标参数
systemd --version
man systemd.timer

这个实验假设要每天执行一次缓存预热脚本。脚本本身应当可重复执行,并能在多个实例偶尔重叠时保持安全。随机延迟只降低碰撞概率,不提供分布式锁,也不保证不同主机绝对不会在同一秒触发。

步骤一:创建一次性 service unit

timer 负责决定何时激活,真正的命令放在同名 service 中。创建 /etc/systemd/system/cache-warmup.service:

# /etc/systemd/system/cache-warmup.service
[Unit]
Description=预热应用缓存

[Service]
Type=oneshot
# 使用固定绝对路径,避免依赖交互式 Shell 的 PATH。
ExecStart=/usr/local/sbin/cache-warmup

Type=oneshot 适合运行完成后退出的批处理任务。文件名为 cache-warmup.service,后面创建同名 cache-warmup.timer 后,timer 默认就会激活这个 service,不需要额外写 Unit=。

步骤二:先确定没有随机化的基准计划

先把基准计划写清楚,再设置错峰窗口。下面的日历表达式表示每天 02:00:00。可以用 systemd-analyze calendar 检查表达式解析结果,这一步只验证日历条件,不会展示最终随机偏移。

# 检查 OnCalendar 表达式会匹配哪些基准时刻
systemd-analyze calendar '*-*-* 02:00:00'

如果每台机器的基准时刻不同,就很难判断随机窗口到底覆盖哪里。保持统一的 OnCalendar=,再用 RandomizedDelaySec= 分散实际触发,配置更容易审计。

步骤三:在 timer 中加入随机延迟窗口

创建 /etc/systemd/system/cache-warmup.timer。以下配置把每日 02:00 作为基准,并把每次执行随机推迟 0 到 30 分钟:

# /etc/systemd/system/cache-warmup.timer
[Unit]
Description=每天错峰执行缓存预热

[Timer]
# 每天 02:00 是基准时刻,实际执行会在此后随机延迟。
OnCalendar=*-*-* 02:00:00
RandomizedDelaySec=30min
# 缩小事件合并窗口,让 30 分钟随机范围真正用于分散触发。
AccuracySec=1s
# 关机期间错过计划时,启动后补一次任务。
Persistent=true

[Install]
WantedBy=timers.target

官方文档说明,RandomizedDelaySec= 默认是 0,也就是不增加随机延迟。随机值会加在“下一次基准触发时间”和“服务管理器启动时间”两者中较晚的那个时间点上。对于普通日历 timer,可以把它理解为:先得到计划时刻,再向后推迟最多 30 分钟。

systemd timer 中 OnCalendar RandomizedDelaySec AccuracySec 与 Service Unit 的静态配置关系
图1:OnCalendar、随机延迟窗口与服务激活对象的静态配置关系说明图。

AccuracySec= 不是另一个随机窗口。它允许 systemd 把邻近的 timer 事件合并,以减少系统唤醒次数;RandomizedDelaySec= 则用于主动拉开相似 timer 的触发时间。官方建议在希望最大化分散效果时把 AccuracySec= 设得很小。这里采用 1 秒,已经适合多数分钟级错峰任务;若需要严格遵循最小合并窗口,可以按官方建议评估 1us。

步骤四:重新加载并启用 timer

写入两个 unit 后,让 systemd 重新读取配置,并同时启用、启动 timer:

# 重新加载 unit 文件,然后立即启动并设置为开机启用
sudo systemctl daemon-reload
sudo systemctl enable --now cache-warmup.timer

enable --now 同时完成两个动作:创建开机启用所需的链接,并在当前系统中启动 timer。只运行 enable 不等于当前立即开始等待;只运行 start 也不会自动获得下次开机启用状态。

步骤五:检查下一次触发时间与参数

启用后先看 timer 列表中的 NEXT 字段,再用 systemctl show 查看底层属性。list-timers 展示的是 systemd 已计算的下一次触发时间,因此它已经包含本轮随机延迟和精度处理的结果。

# 查看下一次实际触发时间,并核对随机延迟相关属性
systemctl list-timers cache-warmup.timer
systemctl show cache-warmup.timer \
  -p NextElapseUSecRealtime \
  -p RandomizedDelayUSec \
  -p AccuracyUSec \
  -p FixedRandomDelay

unit 文件里的高层配置名通常以 Sec 结尾,而 systemctl show 暴露的底层 D-Bus 属性常以 USec 结尾。两者表达同一个设置,只是底层属性使用微秒单位。若 NEXT 不在预期范围,优先检查 OnCalendar= 是否写对、旧 drop-in 是否覆盖配置,以及 timer 是否已经重新加载。

任务触发后,用 service 日志确认脚本是否成功执行:

# 查看本次和历史执行日志,失败时保留完整上下文
journalctl -u cache-warmup.service --since today
systemctl status cache-warmup.service

扩展实验:需要稳定错峰时启用 FixedRandomDelay

默认情况下,timer 每次迭代都会重新选择随机延迟。若希望同一台机器上的同一个 timer 总是保持稳定偏移,可以加入 FixedRandomDelay=true:

# /etc/systemd/system/cache-warmup.timer.d/stable-offset.conf
[Timer]
# 在 RandomizedDelaySec 窗口内,为该主机和 unit 保持稳定偏移。
FixedRandomDelay=true

这个固定值由 machine ID、服务管理器的用户标识以及 timer unit 名称共同派生。它只在 RandomizedDelaySec 不为 0 时生效。对批量部署的服务器而言,各主机通常会得到不同偏移,而单机自己的触发抖动会更小。

systemd 多主机 machine-id FixedRandomDelay 与稳定偏移的静态关系
图2:多主机按身份与 unit 名称形成稳定随机偏移的静态关系说明图。

启用 drop-in 后仍需重新加载并重启 timer,才能重新计算等待状态:

# 应用 drop-in,并让 timer 使用新的固定随机策略
sudo systemctl daemon-reload
sudo systemctl restart cache-warmup.timer
systemctl list-timers cache-warmup.timer

Persistent、AccuracySec 与随机延迟怎么配合

参数解决的问题推荐判断
OnCalendar=定义基准计划先用 systemd-analyze calendar 验证
RandomizedDelaySec=30min把相似任务分散到一个窗口窗口应小于业务允许的最晚完成时间
AccuracySec=1s控制 systemd 合并 timer 事件的范围强调错峰时设小,强调省电时可保留较大值
FixedRandomDelay=true让同一 timer 的随机偏移保持稳定批量主机希望固定分片时启用
Persistent=true机器关机期间错过日历任务后补跑任务允许开机后补执行时启用

Persistent=true 可能让多台刚开机的机器都产生补跑需求,而随机延迟可以继续降低它们同时启动任务的概率。但它仍不是集中式协调器:如果任务对并发有硬限制,还应在服务端设置队列、租约或互斥机制。

清理实验 unit

不再需要该实验时,先停用 timer,再删除 unit 和 drop-in,最后重新加载 systemd。不要只删除文件却留下仍在内存中等待的 timer。

# 停止并取消开机启用,然后移除本次实验配置
sudo systemctl disable --now cache-warmup.timer
sudo rm /etc/systemd/system/cache-warmup.timer
sudo rm /etc/systemd/system/cache-warmup.service
sudo rm -rf /etc/systemd/system/cache-warmup.timer.d
sudo systemctl daemon-reload

相关问题

RandomizedDelaySec 会提前执行任务吗?

不会。它是在已确定的基准触发时间之后增加非负随机延迟,不会把任务提前到 OnCalendar 之前。

为什么多台主机仍可能同时执行?

各 timer 独立选择随机值,不存在跨主机协调,因此只能降低碰撞概率,不能保证完全不重合。窗口太小或主机数量很多时,仍应使用服务端限流或分布式锁。

为什么修改 timer 后 NEXT 没变化?

先执行 systemctl daemon-reload,再重启 timer。还要用 systemctl cat cache-warmup.timer 检查发行版自带 unit 或旧 drop-in 是否覆盖了当前配置。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>