了解 TimescaleDB 背景工作者和作业
Source: Dev.to
请提供您希望翻译的完整文本内容(除代码块和 URL 之外),我会将其翻译成简体中文并保持原有的格式、Markdown 语法和技术术语不变。谢谢!
Source: …
背景工作者的工作机制
每次调用 add_compression_policy()、add_retention_policy() 或 add_continuous_aggregate_policy() 时,TimescaleDB 都会注册一个计划任务。每个任务都在 PostgreSQL 背景工作者中运行——这是一个独立的进程,运行在任何客户端连接之外。
可用背景工作者的数量受两个参数控制:
| 设置 | 描述 | 默认 |
|---|---|---|
timescaledb.max_background_workers | TimescaleDB 自己调度器的上限 | 8‑16 |
max_worker_processes | PostgreSQL 的全局限制,供所有扩展、并行查询和逻辑复制共享 | — |
您可以使用以下查询检查当前配置:
SELECT name, setting, unit
FROM pg_settings
WHERE name IN (
'timescaledb.max_background_workers',
'max_worker_processes',
'max_parallel_workers'
)
ORDER BY name;
并使用下面的查询列出所有活动任务:
SELECT
job_id,
proc_name,
hypertable_name,
schedule_interval,
scheduled AS is_active
FROM timescaledb_information.jobs
WHERE scheduled = true
ORDER BY proc_name, hypertable_name;
Source: …
疲劳是如何产生的
数学计算直接且毫不留情。
- 每个带有压缩、保留 且 连续聚合的 hypertable 会产生 三个 作业。
- 八个 hypertable → 24 个作业。
- 再加上 TimescaleDB 的内部维护任务,总数很容易超过 100 个计划作业。
默认的 max_background_workers 为 16,根本不是为这种负载设计的。
当作业无法获取 worker 时,它 不会 抛出错误,只是停留在调度器的队列中。如果在调度间隔内没有空闲 worker,下一次调用就会排在当前作业后面。随着时间推移,积压会形成,并在每个周期中进一步叠加。
最坏情况: 作业的执行时间超过了它的调度间隔。
一个需要 15 分钟完成但每 10 分钟调度一次的压缩作业会永久占用一个 worker 插槽,永远追不上进度。每个周期都会再加入一个排队的调用,导致其他作业类型被饿死。
诊断问题
将作业计数与工作者限制进行比较
WITH worker_config AS (
SELECT current_setting('timescaledb.max_background_workers')::int AS max_workers
),
active_jobs AS (
SELECT count(*) AS total_scheduled_jobs
FROM timescaledb_information.jobs
WHERE scheduled = true
)
SELECT
wc.max_workers,
aj.total_scheduled_jobs,
CASE
WHEN aj.total_scheduled_jobs = 0 THEN 'FAILING'
WHEN js.total_runs = 0 THEN 'NEVER RUN -- likely queued'
ELSE 'OK'
END AS health_status
FROM timescaledb_information.jobs j
JOIN timescaledb_information.job_stats js ON j.job_id = js.job_id
ORDER BY js.total_failures DESC, j.proc_name;
关键警告信号
total_runs = 0– 作业已注册但从未获取到工作者(纯队列饥饿)。consecutive_failures上升 – 工作者已获取但作业失败(通常是锁争用或压缩期间的 OOM)。last_run_duration>schedule_interval– 作业在下一次调用前永远无法完成,永久阻塞工作者槽位。
合理调配工作池规模
公式很简单:
total_policies + 2 (internal jobs) = minimum max_background_workers
使用以下查询计算精确的建议值:
WITH policy_count AS (
SELECT count(*) AS total_jobs
FROM timescaledb_information.jobs
WHERE scheduled = true
)
SELECT
total_jobs,
total_jobs + 2 AS recommended_workers,
'ALTER SYSTEM SET timescaledb.max_background_workers = '
|| (total_jobs + 2) AS sql_to_run
FROM policy_count;
应用更改:
ALTER SYSTEM SET timescaledb.max_background_workers = 28;
ALTER SYSTEM SET max_worker_processes = 32;
-- Requires a full PostgreSQL restart – pg_reload_conf() is NOT sufficient
过度配置成本低。每个空闲的后台工作进程大约消耗 5‑10 MB 内存且不占用 CPU。将 max_background_workers 设置为 32 或 64,即使服务器上运行 20 个作业,也不会产生可测量的性能损失。相反,配置不足会悄然破坏整个自动化流水线。
关于 PostgreSQL 18 + TimescaleDB 2.24 的说明
如果您在使用 PostgreSQL 18 与 TimescaleDB 2.24,需注意通过 add_job() 注册的自定义函数会出现 “cache lookup failed for function” 错误。该版本组合下的后台工作进程无法解析 public‑schema 中的函数。解决办法是将任何自定义的计划任务交给系统 cron 处理,而让 TimescaleDB 正常执行其内置策略(压缩、保留、聚合刷新)。
Source: …
防护清单
-
在每个新 hypertable 之后统计作业数量。
每个带完整策略的 hypertable 会添加 3 个作业。请主动更新 worker 设置。 -
定期监控
total_failures和consecutive_failures。
每周查询timescaledb_information.job_stats,或设置自动化监控。 -
确保作业执行时长低于调度间隔。
当last_run_duration接近schedule_interval时,考虑:- 增加间隔时间,
- 减小块大小,或
- 添加更多 worker。
-
将
max_worker_processes设置为高于max_background_workers。
为并行查询和逻辑复制留出余量。 -
记住:这两个设置都需要完整的 PostgreSQL 重启。
请在维护窗口期间进行更改。 -
把 worker 大小视为 hypertable 设置的一部分。
添加策略 → 添加 worker。切勿把它当作事后补救。
背景 worker 耗尽是完全可以预防的。修复只需一条 SQL 语句和一次重启。难点在于在自动化静默停止之前就能发现并处理它。