快速回顾一下,autovacuum后台进程清理死行,例如旧的已删除行版本。您也可以通过运行手动执行清理VACUUM,但autovacuum会根据表中死行的数量在适当的时候自动执行清理,不要太频繁但足够频繁以控制“垃圾”的数量。

一般来说,autovacuum不能运行得太频繁,只有在表中累积了一定数量的死行后才会执行清理。但它可能会由于各种原因而延迟,导致表和索引变得比预期的要大,这正是这篇文章的主题。那么常见的罪魁祸首是什么以及如何识别它们呢?

节流

autovacuum worker 被限制为每个时间间隔只能执行一定数量的工作。默认限制相当低,大约 4MB/s 的写入,8MB/s 的读取。这适用于像 Raspberry Pi 这样的微型机器或 10 年前的小型服务器,但现在的机器更强大(在 CPU 和 I/O 方面)并且处理更多的数据。

假设您有几张大表和几张小表。如果所有三个autovacuum工作人员都开始清理大表,则无论小表累积了多少死行,都不会清理任何小表。假设您有足够的监控,识别这并不是特别困难。寻找所有autovacuum工作人员都忙碌而尽管累积了许多死行但表未被清理的时间段。

所有必要的信息都在 pg_stat_activity ( autovacuum worker进程数) and pg_stat_all_tables (last_autovacuum and n_dead_tup)。

增加autovacuum工人数量不是解决方案,因为总工作量保持不变。您可以指定每个表的限制,从总限制中排除该工作人员,但这仍然不能保证在需要时会有可用的工作人员。

正确的解决方案是调整节流,使用与硬件配置和工作负载模式相关的合理限制。如果你能减少数据库中产生的死行数量,那将是一个理想的解决方案。

从这一点来看,我们假设节流不是问题,即工作autovacuum人员不会长时间饱和,并且在所有表上触发清理而不会出现不合理的延迟。

长事务

所以,如果定期清理表,肯定不会积累很多死行,对吧?很不幸的是,不行。这些行在被删除后实际上并不是立即“可移动”的,但只有在没有可能看到它们的事务时才可以。确切的行为取决于其他事务正在(正在)做什么和序列化级别,但一般来说:

读已提交

  • 运行查询块清理
  • 空闲事务仅在执行写入时才阻止清理
  • 空闲事务(没有任何写入)不会阻止清理(但无论如何保留它们不是一个好习惯)

可序列化

  • 运行查询块清理
  • 空闲事务块清理(即使它们只进行了读取)

在实践中它当然更细微,但解释所有不同的位需要首先解释 XID 和快照是如何工作的,这不是本文的目标。您真正应该从中得出的结论是,长事务不是一个好主意,尤其是如果这些事务可能已完成写入。

当然,您可能需要长时间保留事务是有充分正当理由的(例如,如果您需要确保所有更改的 ACID)。但要确保它不会在不必要的情况下发生,例如由于应用程序设计不佳。

一个有点出乎意料的结果是高 CPU 和 I/O 使用率,由于autovacuum一遍又一遍地运行,没有清除任何死行(或只是其中的一些)。因此,这些表仍然有资格在下一轮进行清理,弊大于利。

如何检测这个?首先,您需要监控长时间运行的事务,尤其是空闲事务。您需要做的就是从pg_stat_activity. 视图定义随着 PostgreSQL 版本发生了一些变化,因此您可能需要稍微调整一下:

SELECT xact_start, state FROM pg_stat_activity;

-- count 'idle' transactions longer than 15 minutes (since BEGIN)
SELECT COUNT(*) FROM pg_stat_activity
 WHERE state = 'idle in transaction'
  AND (now() - xact_start) > interval '15 minutes'

-- count transactions 'idle' for more than 5 minutes
SELECT COUNT(*) FROM pg_stat_activity
 WHERE state = 'idle in transaction'
  AND (now() - state_change) > interval '5 minutes'

您也可以简单地使用一些现有的监控插件,例如check_postgres.pl。那些已经包括这种类型的健全性检查。您必须决定什么是合理的事务/查询持续时间,这是特定于应用程序的。

从 PostgreSQL 9.6 开始,您还可以使用idle_in_transaction_session_timeout自动终止空闲时间过长的事务。同样,对于长查询,有statement_timeout

另一个有用的东西是VACUUM VERBOSE它实际上会告诉你有多少死行还不能被删除:

db=# VACUUM verbose z;
INFO:  vacuuming "public.z"
INFO:  "z": found 0 removable, 66797 nonremovable row versions in 443 out of 443 pages
DETAIL:  12308 dead row versions cannot be removed yet.
...

它不会告诉您哪个后端阻止了清理,但它是正在发生的事情的一个非常清楚的标志。

注意:您无法轻松地从中获取此信息,autovacuum因为它仅在默认情况下记录DEBUG2(并且您肯定不希望在生产中使用该日志级别运行)。

热备长查询

让我们假设表被及时清理,但没有删除死元组,导致表和索引膨胀。您正在监控pg_stat_activity并且没有长时间运行的事务。可能是什么问题?

如果您有一个流式副本,那么问题很可能就在那里。如果副本使用hot_standby_feedback=on,则副本上的查询几乎就像主服务器上的事务一样,包括阻塞清理。当然,hot_standby_feedback=on在对副本运行长查询(例如分析和 BI 工作负载)时恰好使用它,以防止由于复制冲突而取消。

遗憾的是,您必须做出选择,要么保留hot_standby_feedback=on并接受清理延迟,要么处理取消的查询。您也可以使用它max_standby_streaming_delay来限制影响,尽管这并不能完全阻止取消(因此您仍然需要重试查询)。

实际上,现在还有第三种选择,逻辑复制。您可以使用 PostgreSQL 10 中提供的新逻辑复制来复制更改,而不是对 BI 副本使用物理流复制。逻辑复制放松了主副本之间的耦合,并使集群大部分独立(独立清理等等)。

这解决了与物理流复制相关的两个问题,延迟清理主数据库或取消 BI 副本的查询。不过,对于服务于 DR 目的的副本,流式复制仍然是正确的选择,但是那些副本没有(或不应该)运行长时间查询。

注意:虽然我提到逻辑复制将在 PostgreSQL 10 中可用,但基础设施的很大一部分在以前的版本中可用(特别是 PostgreSQL 9.6)。因此,您甚至可以在旧版本上执行此操作(我们为我们的一些客户这样做),但 PostgreSQL 10 将使它更加方便和舒适。

糟糕的autoanalyze

您可能会错过的一个细节是,autovacuum工人实际上执行两项不同的任务。首先是清理(就像运行一样VACUUM),而且还收集统计信息(就像运行一样ANALYZE)。并且这两个部分都使用autovacuum_cost_limit

但是在处理事务上有很大的不同。每当VACUUM部分到达时autovacuum_cost_limit,工作人员释放快照并休眠一段时间。但是ANALYZE必须在单个快照/事务中运行,这会阻止清理。

这是搬起石头砸自己脚的优雅方式,特别是如果你也这样做的话:

  • 增加default_statistics_target以从更大的样本中构建更准确的统计数据
  • 降低autovacuum_analyze_scale_factor以更频繁地收集统计信息

意想不到的后果当然是ANALYZE会更频繁地发生,需要更长的时间并且(与VACUUM部分不同)会阻止清理。解决方案通常相当简单,不要降低autovacuum_analyze_scale_factor太多。每次运行ANALYZE10% 的表更改在大多数情况下应该绰绰有余。

n_dead_tup

我想提及的最后一件事是关于pg_stat_all_tables.n_dead_tup值的变化。您可能认为该值是一个简单的计数器,每当创建新的死元组时递增,并在清除时递减。但它实际上只是对死元组数量的估计,由ANALYZE. 对于小表(小于 240MB),差别不大,因为ANALYZE读取整个表,所以非常准确。然而,对于大型表,它可能会发生很大变化,具体取决于对表的哪个子集进行采样。降低autovacuum_vacuum_scale_factor使其更加随机。

n_dead_tup所以在监控系统中查看时要小心。值的突然下降或增加可能仅仅是由于ANALYZE重新计算不同的估计,而不是由于实际清理和/或表中出现新的死元组。

总结

总结为几个简单的要点:

  • autovacuum只有在没有可能需要死元组的事务的情况下才能完成它的工作。
  • 长时间运行的查询会阻止清理。考虑使用statement_timeout以限制查询。
  • 长时间运行的事务可能会阻止清理,确切的行为取决于隔离级别或事务中发生的事情,监视它们并在可能的情况下终止它们。
  • 对副本的长时间运行查询hot_standby_feedback=on也可能会阻止清理。
  • autoanalyze也受到限制,但与VACUUM不同,它保留单个快照(因此会阻止清理)。
  • n_dead_tup只是ANALYZE维护的估计值,因此预计会有一些波动(尤其是在大表上)。
posted on 2024-01-15 22:14  jl1771  阅读(20)  评论(0编辑  收藏  举报