通常,在焦急地查看发行说明或浏览 git 日志后,人们会在发布日期欣赏 PostgreSQL 的新功能。每隔一段时间,尤其是在处理旧版本的 PostgreSQL 时,您会发现一个功能没有引起您的注意,但当您发现它时,您会更高兴它。
我最近在客户端的 PostgreSQL 集群上遇到了一些奇怪的性能问题 。它们自托管在 AWS 实例/硬件上,使用从主服务器克隆的副本,使用 pg_basebackup 并使用相同的机器类型创建,因此它具有相同的 IOPS、CPU 数量和 RAM。然而 ,与主要查询相比,特定查询副本的性能要差几个数量级。此外,副本上有大量挂钩的后端进程。
然后,客户端 对有问题的表执行手动 VACUUM ANALYZE,这导致主数据库和副本服务器的目标查询性能都恢复到 <1 毫秒(与之前的主数据库和副本数据库的约 10 毫秒相比)和负载问题解决。
我们现在的任务是解释数据并解释如何防止此问题发生。
一些分析
由于机器具有相同的规格、配置和 pg_statistics 数据(由于是物理复制集群的一部分),这意味着给定查询的计划在主或副本上将是相同的。这进一步表明可能的罪魁祸首是副本将不同的数据加载到shared_buffers 中,因此我们遇到了典型的冷缓存类型的情况。相同查询的后续运行应该以类似的性能特征证明这一点。
那么为什么以前的性能很差,我们可以改进它吗?手动 VACUUM 运行后,主要和副本的性能显着提高,表明可能正在使用仅索引扫描。因此需要在副本上加载更少的数据,因此我们不再受 IO 限制。这很重要,因为除了清理死元组之外,VACUUM 还会更新关系的可见性映射,该映射需要相对最新,以便规划者考虑仅进行 INDEX 扫描。
这也很奇怪,因为客户有定期的自动吸尘器设置,所以大概应该有定期的吸尘器以保持最新状态。
拼图的缺失部分
拼图的缺失部分是这些表是仅插入的。在 13 之前的 PostgreSQL 版本中,不会 以仅插入的工作负载启动autovacuum,这大概是因为如果没有更新或删除,就没有需要回收的可用空间。一个utoanalyze 被 踢掉仅在插入的工作负载,因为有新的数据,并且可以收集新的统计数据。由于 autovacuum 未在此表上运行,因此 Visibility Map 未更新,因此查询性能开始随着时间的推移而受到影响。更新可见性地图后,性能就会恢复。
- 允许插入(不仅是更新和删除)触发 autovacuum 中的清理活动(Laurenz Albe、Darafei Praliaskouski)
- 以前,仅插入活动会触发自动分析,但不会触发自动清空,因为不能删除任何死元组。然而,真空扫描还有其他有用的副作用,例如设置页面所有可见位,这提高了仅索引扫描的效率。此外,允许仅插入表接受定期清理有助于分散“冻结”旧元组的工作,这样当整个表达到反环绕阈值时,不会突然有大量的冻结工作要做立刻。
- 如有必要,可以使用新参数 autovacuum_vacuum_insert_threshold 和 autovacuum_vacuum_insert_scale_factor 或等效的表存储选项调整此行为。
所以这既解释了所看到的问题,也说明了它在 PostgreSQL 13+ 的未来版本中是如何解决的。
解决方案
那么客户现在有哪些选择呢?
- 升级到 PostgreSQL 13+。虽然这在路线图上,但这是需要计划和协调的事情,而不是直接的解决方案。
- 预定的外部 VACUUM。由于 autovacuum 没有为给定的表明确捕获此信息,因此请使用 cron 并 在相关表上安排定期运行 VACUUM ANALYZE。这里的频率取决于工作负载,但一个好的猜测是每周运行一次,看看是否需要更频繁地移动。
- 表存储参数调整。现在我们确实说过没有办法强制 autovacuum 对特定的表进行吸尘,但这并不完全正确。我们可以(相当粗暴地)调整相关表的 autovacuum_max_freeze_age 参数,以启动 环绕预防真空,这具有运行我们想要的真空类型的副作用。此参数可以设置为低至 100k,并确定数据库在强制执行真空之前允许表获得多少旧事务。这个阈值只能在这张桌子上设置(如果你要走这条路,可能应该如此)。这确实意味着这些清理完全基于数据库中的事务数量作为一个整体,即使此表没有任何更改,此自动清理仍会以该参数确定的频率运行。
综上所述
简而言之,如果您在 13 之前的 PostgreSQL 版本中有仅插入表,您可以从运行定期调度的VACUUM 中受益 。




