监视自动清理

Autovacuum 完成三项任务:回收死亡元组占用的空间,维护可见性映射以便 PostgreSQL 在扫描时跳过某些页面,以及推进事务 ID 视界以防止回卷。 当它跟不上时,其影响就会在系统其他各处显现出来,表现为 CPU 飙高、IOPS 异常、查询变慢等;这就是为什么本指南往往会成为一场原本从别处开始的排查的最终落脚点。

回卷是这里唯一一种会使写入完全停止的故障模式。 其他一切都会逐渐退化。

本指南介绍的症状

  • 查询计划随时间推移而降级,无需更改查询
  • 表或索引的大小增长速度快于其中的数据
  • 工作负载保持平稳,而 CPU 或 IOPS 上升
  • 事务 ID 年龄正在逼近回卷阈值
  • DELETE 未回收的存储占用

开始之前

启用 PostgreSQL 服务器日志、 Autovacuum 和架构统计信息以及 剩余事务;设置为 log_autovacuum_min_duration 非负值;并启用 metrics.collector_database_activity 增强的指标选项卡。请参阅 “使用故障排除指南”。

Important

只读副本上的 Autovacuum 统计信息反映主副本的活动,并可能误导。 针对 主要数据库运行本指南。

范围和限制

在将数字与你自己的查询进行比较之前,请阅读这些内容:

  • 本指南仅显示具有 100 多个实时元组和 1,000 多个死元组的数据库和架构。 它会过滤掉小表,因此总计与 pg_stat_user_tables 不一致。
  • 本指南按照数据库创建(OID)顺序分析每台服务器最多 30 个数据库;在 Burstable 实例上,则最多分析 10 个数据库。 对于超出上限的服务器,该指南会无提示地排除数据库,但在发生这种情况时会发出警告。
  • 最短时间范围为 1 小时。

本指南的组织方式

选项卡 它所回答的问题
膨胀 有多少未使用空间?
元组 哪个活动正在创建它?
吸尘和分析 是否完全遗漏了什么?
Autovacuum 运行情况 autovacuum 在整个服务器范围内是否跟得上?
每个表的自动清理 哪些表有问题?
增强型指标 我距离回绕还有多近?
真空节流概述 基于成本的节流是否拖慢了 VACUUM 的速度?
见解 自动检测到了什么?
Configuration 参数是否正确?

指南顶部 的数据库 选择器将每个选项卡的范围限定为所有数据库或所选数据库。

演练:繁忙表上的膨胀现象

  1. 量化它。 “膨胀”选项卡显示一个数据库,其膨胀率高于 100%,这意味着死元组比活元组多。 这是严重的问题,不是小问题。

  2. 了解驱动程序。元组 显示该数据库上的大量更新活动。 在 PostgreSQL 中,每个 UPDATE 都会创建一个新的行版本,并将旧版本标记为已失效,因此按设计,更新频繁的表会持续产生膨胀。 问题在于 autovacuum 是否跟得上,而不在于是否正在产生膨胀。

  3. 检查是否存在空缺。清理和分析显示,相当一部分表从未进行过自动清理。 某种因素正在阻止 autovacuum 访问到它们。

  4. 检查容量。自动清理活动显示工作线程饱和提示,表示所有 autovacuum_max_workers 都至少有一次处于忙碌状态。 工作被排入队列,等待可用的工作进程处理。

  5. 查找原因。按表统计的 Autovacuum 显示,少数几个非常大的表会长时间占用工作进程,导致其他任务得不到资源。 它还显示这些表上的 索引扫描 次数超过 1,这意味着 maintenance_work_mem 太小,每次清理都要对索引进行多次遍历,做了数倍于必要的工作。

  6. 排除更糟糕的解释。增强的指标 显示最旧的后端 xmin 正常推进,因此不会阻止清理。 这是吞吐量问题,而不是阻塞的地平线问题。 如果 xmin 呈平稳状态,你就到此为止,改为前往 排查 autovacuum 阻塞因素和回卷风险。

  7. 行动。提高maintenance_work_mem,使清理可在一次索引扫描中完成;对少数几个大表应用激进的按表 autovacuum 设置,使其能以更高频率、较小增量进行清理;并同时提高autovacuum_vacuum_cost_limit和autovacuum_max_workers,这样新增的工作进程才能真正获得 I/O 预算。

步骤 6 的分支点才是需要真正理解的关键:持续推进 xmin 意味着是吞吐量问题;停滞不前 xmin 则意味着存在阻塞性问题。 两者需要完全不同的解决方法。

选项卡参考

膨胀

膨胀比率为 dead_tuples / live_tuples * 100. 可超过 100%:

比率 阅读
大约 50% 死元组的数量是活元组的一半。 值得注意。
大约 100% 死元组与存活元组数量相等。 重要。
超过 100% 半死不活。 严重,因为扫描结果大多是乱码。

从服务器级别逐级深入到数据库,再深入到各个架构。 高架构级别比率有两个解释,必须区分它们:

元组

存活元组与死元组,以及产生它们的插入、更新和删除活动:

  • 更新 是最大的膨胀源。 每个都会写入一个新的行版本,并将旧版本标记为失效。 具有许多索引的表更糟,因为每个索引也需要更新。
  • 删除会留下在经过 vacuum 处理之前无法重新使用的空间。
  • 插入操作会在表已存在碎片时加剧这一情况。

死元组数量呈上升趋势并推高膨胀率,这就是该采取行动的信号。

清空和分析

跟踪每个数据库中被清理和分析的表数量。 关注 从未执行自动清理的表 [%] 和 从未执行自动分析的表 [%]。

从未自动清理比例较高的常见原因:

  1. 数据库过多,autovacuum 无法全部处理。 Azure Database for PostgreSQL 灵活服务器中的自动清理优化。
  2. 自动清理速度太慢。 Azure Database for PostgreSQL 灵活服务器中的自动清理优化。
  3. 被大桌子垄断的工人,饿死别人。 应用表专用设置。
  4. 值为零可能意味着 autovacuum 完全被禁用。 检查横幅。
  5. 升级或迁移后,统计信息初始为空。 在恢复工作负荷之前,请运行数据库范围的手动清扫和分析。

对于从未自动分析的比例较高的情况,请考虑采用更激进的 autovacuum_analyze_scale_factor 和 autovacuum_analyze_threshold。 过时的统计信息会导致糟糕的执行计划,而这往往是用户最先察觉到的症状。

自动清理活动

整个服务器范围内的自动清理会在该时间窗口内运行。 使用趋势图查找峰值,使用触发原因细分来区分常规运行与 防回卷 和 故障保护 运行,并使用按数据库划分的表格查看负载位于何处。

防回绕和故障保护运行测试并非常规操作。 这意味着正常清理已经无法跟上,PostgreSQL 现在正在保护自身。

如果出现 工作进程饱和 横幅,则表示 autovacuum 至少有一次达到 autovacuum_max_workers,并且相关工作发生了等待。 将autovacuum_vacuum_cost_limit与autovacuum_max_workers一起抬起。 成本上限由各个工作线程共享,因此,如果只增加工作线程而不提高这一上限,就只是把相同的 I/O 预算分摊到更多线程上。

每个表的自动清理

表级详细信息。 要求 log_autovacuum_min_duration 为非负值。

列 含义
元组已死但未删除 已死但无法清理的行,因为打开的快照仍可能需要它们。 持续存在的非零值表明存在阻塞性问题。 转到 排查 autovacuum 阻塞因素和回卷风险。
防回绕运行 绕过成本限制以防止 XID 耗尽的真空。 任何非零值都值得调查。
索引扫描 每次运行的索引遍数。 以上 1 表示 maintenance_work_mem 太小,真空不必要地重复工作。 通常是最便宜的解决办法。

增强式指标

需要 metrics.collector_database_activity。

已使用的最大事务 ID PostgreSQL 在包装之前有大约 20 亿个可用事务 ID,此时它停止接受写入:

价值 地位 Action
低于 2 亿 正常 None
2 亿到 10 亿 调查 防缠绕吸尘器正在运行。 找出是什么阻碍了例行清理。
超过 10 亿 危急 立即采取行动。 在此阈值处设置指标警报。

在 PostgreSQL 14 及更高版本中,故障保护会在 vacuum_failsafe_age(默认值为 16 亿)时触发,并完全绕过成本节流机制以避免停机。 如果故障保护机制已启动,就说明离服务中断不远了。

最旧的后端 xmin。 后端的最早快照范围。 VACUUM 无法删除比该版本更新的任何行版本,因此停滞的 xmin 会阻止清理,无论 autovacuum 运行得多频繁:

  • 以稳定的吞吐量持续推进是健康的。
  • 平稳或几乎不动表示存在阻塞问题。 最有可能是长时间运行的事务、事务内空闲会话、孤立的已准备事务或非活动复制槽。 转到 排查 autovacuum 阻塞因素和回卷风险。

这一个指标是最快区分吞吐量问题和阻塞问题的方法。

真空节流概述

基于成本的节流会使 vacuum 工作进程暂停。 你可以通过 VacuumDelay wait 看到这一暂停。 若要获得较精细的分辨率,请使用 pgms_wait_sampling。 若要查看较粗略的五分钟视图,请使用 pg_stat_activity 快照。 某些限流是有意为之的。 它可防止 vacuum 操作抢占工作负载的 I/O 资源。 仅在限流持续存在且同时伴随膨胀加剧、XID 年龄上升或 autovacuum 跟不上时,才进行调优。

  • 优先选择按表设置来处理少数高流量表,而不要更改服务器范围内的默认设置。
  • 每次只更改一项,并确认限流减少,且不会引入 CPU 或 I/O 压力。

Configuration

当前的 autovacuum 参数、默认值和修改方式。

注释

值为服务器级别。 通过 ALTER TABLE ... SET (autovacuum_...) 进行的各表设置

autovacuum 无法提供的操作指导

某些情况下,无论如何调优,都仍然需要计划内维护。 使用 pg_cron 或自定义调度器。

情况 计划内容和原因
分区表 Autovacuum 处理子分区,但不自动分析父分区,因此父统计信息过时,计划会降级。 日程 ANALYZE parent_table;。 在 PostgreSQL 18 及更高版本中,ANALYZE ONLY parent_table; 仅刷新父表。
仅插入表、PostgreSQL 12 及更早版本 这些版本在执行插入操作时根本不会触发 autovacuum。 日程 VACUUM (FREEZE, ANALYZE) my_table;。 在 PostgreSQL 13 及更高版本中,请改为调整 autovacuum_vacuum_insert_threshold 和 autovacuum_vacuum_insert_scale_factor。
批量加载后 在大型COPY或批量INSERT操作后立即运行ANALYZE my_table;,以便规划器无法通过预加载统计信息工作。

仅靠调优无法解决的臃肿问题:

  • 清理被阻止。 任何 autovacuum 配置都无法解决被固定住的 xmin horizon。 请参阅 对 autovacuum 阻止器和包装风险进行故障排除。

  • TOAST 表。 较大的 text 和 jsonb 值存储在单独的 TOAST 表中,并具有其自身的 toast.autovacuum_* 存储参数。 那里的膨胀在常规表统计信息中是不可见的。

  • 索引膨胀。 VACUUM 回收表空间,但不收缩索引。 定期对频繁更新的索引运行 REINDEX CONCURRENTLY。 调优首选项:

  • 优先为少数确实需要这些设置的繁忙表单独设置:ALTER TABLE t SET (autovacuum_vacuum_scale_factor = 0.01, autovacuum_vacuum_threshold = 1000);

  • 对于更新频繁的表,可将 fillfactor 降低到大约 80 或 90,以启用更多的 HOT 更新,从而减少表膨胀和索引维护开销。

  • 不要禁用 autovacuum。 它不会阻止强制的反包装真空,膨胀累积,直到事情变得更糟。 改为调整它。

  • 将 log_autovacuum_min_duration 设置为类似 10000 的值(10 秒),以捕获长时间运行的情况,并在 pg_stat_user_tables 中观察 n_dead_tup、n_mod_since_analyze、last_autovacuum 和 last_autoanalyze。

优秀的标准是什么样的

信号 正常 调查
膨胀率 低且稳定 高于 50%,或呈上升趋势
最旧的后端 xmin 以吞吐量推动发展 平坦
已使用的最大事务 ID 低于 2 亿 超过 2 亿
防回绕运行 零 任意
每个真空的索引扫描数 1 高于 1
从未自动清理的表 近 0% 有意义的百分比
工作器饱和 未报告 横幅已显示
VacuumDelay 节流 简短且偶尔出现 持续存在,且臃肿不断加剧

本指南无法告诉你的内容

  • 索引膨胀。 仅显示表级膨胀情况。 分别评估各个索引。
  • TOAST 臃肿。 不反映在主表的统计信息中。
  • 小型表格。 低于元组阈值的内容已被过滤掉。
  • 数据库数量已超出上限。 尽管指南已对此提出警告,但仍会按 OID 顺序被静默排除。
  • 有效的每表设置。 仅显示服务器级配置。
  • 什么阻碍了清理。 本指南会告知您清理已被阻止。 排查阻碍 autovacuum 的因素和回卷风险会告诉你是什么导致了这种情况。