【问题标题】:Postgresql auto-vacuuming taking too longPostgresql 自动清理耗时过长
【发布时间】:2020-07-13 15:01:49
【问题描述】:

我有大约 5-6 百万个条目的 db 表,执行清理大约需要 20 分钟。由于该表的一个字段更新非常频繁,因此有很多死行需要处理。

据估计,根据我们当前的用户群,每天可能有 200 万个死元组。所以,这个表的吸尘需要两个:

  1. 读取 IO:因为整个表不存在于共享内存中。
  2. 写入 IO:因为有很多条目要更新。

什么是清理这张桌子的理想方法?我是否应该增加 autovacuum_cost_limit 以允许每次 autovacuum 运行更多操作?但正如我所见,它会增加IOPS,这可能会再次阻碍性能。目前,我有autovacuum_scale_factor = 0.2。我应该减少它吗?如果我减少它,它会更频繁地运行,虽然写入 IO 会减少,但它会导致更多的时间段具有高读取 IO。

此外,随着用户群的增加,随着表大小的增加和真空度的增加,将需要从磁盘读取大量数据,这将花费越来越多的时间。那么,我该怎么办?

我想到的解决方案之一:

  1. 将高度更新的列分开,制作单独的表格。
  2. 调整参数以使其更频繁地运行以减少写入 IO(如上所述)。如何处理更多读取 IO,因为真空现在会更频繁地运行?
  3. 将第 2 点与增加 RAM 相结合以减少读取 IO。

一般来说人们采取的方法是什么,因为我假设人们必须有非常大的表 10GB 或更多,这需要被清理。

【问题讨论】:

  • 你的 PostgreSQL 版本是多少?
  • @jjanes 我正在使用 postgresql 11
  • 你的桌子有多大?您稍后提到 10GB 用于假设表,这是您表的大小吗?这是否使平均元组宽度约为 1800 字节?
  • 没有。我的表(不包括索引)大约 1.5 GB(6 百万行)。我想知道如果我在一段时间内达到那个尺寸会发生什么。
  • 我应该将该列分开,以便每个元组的大小对于真空来说很小吗?

标签: postgresql vacuum


【解决方案1】:

有两种方法:

  1. 减少该表的autovacuum_vacuum_cost_delay,以便自动清理变得更快。它仍然会消耗 I/O、CPU 和 RAM。

  2. 将表的 fillfactor 设置为小于 100 的值,并确保您经常更新的列编入索引。然后您可以获得不需要VACUUMHOT 更新

【讨论】:

  • 如果该字段被索引会发生什么?
  • 您不会获得 HOT 更新,最终会出现大量死元组和 autovacuum 运行。
  • 这肯定会改善写入 io 并减少要传输到只读副本的 wal 大小。有什么方法可以提高读取 IO?我应该将更新的列分隔到一个新表中吗?所以在吸尘时会减少读取次数?
  • 您可以考虑将列放在不同的表中。你仍然需要做很多吸尘工作。
  • 我明白,但它会解决很多问题。就一般知识而言,Postgres 是一个使用非常广泛的数据库,人们必须在一个需要清理的表中有数十亿行,有时需要更频繁地清理(比如每天一次)。那么,它会在运行期间消耗机器的整个 IO 吗?
【解决方案2】:

分离列是一种可行的策略,但对我来说是最后的手段。 PostgreSQL 的每行开销已经很高,这样做会使开销翻倍(这也可能会消除大部分好处)。另外,它会让你的查询更难看、更难阅读、更难维护、更容易引入错误。拆分它最有吸引力的地方是,如果对一组不包括此内容的列进行仅索引扫描对您很重要,并且拆分它可以让您将其余列的可见性映射保持在更好的状态。

您为什么关心这需要 20 分钟?这会导致不好的事情发生吗?以这样的速度,您每天可以对这张桌子进行 72 次吸尘,这似乎比实际需要吸尘的次数要多得多。在 v12 中,autovacuum_vacuum_cost_delay 的默认值降低了 10 倍,降至 2ms。默认值的这种变化不是由 v12 中代码的变化驱动的,而是由于在大多数情况下旧的默认值与现代硬件已经过时了。我可以毫不费力地将更改推送到 v11 配置中;但我认为这样做也不会解决您的主要问题。

您实际上对生成的 IO 量有疑问,还是只是猜测?完成的 IO 主要是顺序的,但它的重要性取决于您的存储硬件。当真空发生时,您是否看到延迟峰值?您是否按 IO 收费并且您的账单太高?高IO本来就不是问题,只有出问题了才是问题。

目前,我的 autovacuum_scale_factor = 0.2。我应该减少它吗? 如果我减少它,它将更频繁地运行,尽管写入 IO 会 减少,但它会导致更多的高读取时间段 IO。

如果有的话,更频繁地运行可能不会大幅减少您的写入 IO。每个具有至少一个过时元组的表/索引页都需要在每次清理期间写入。写一页只是为了删除一个过时的元组将导致更多的写入,而不是等到有很多过时的元组可以通过一次写入全部删除。您可能会在每个真空中写得少一些,但做更多的真空将弥补这一点,而且可能远远超过弥补它。

【讨论】:

  • 这并没有造成任何问题,但我担心写入 IO 密集型的事情是它会将 wal 文件传输到读取副本,因为高 io 将具有更高的复制.如果我们谈论读取 IO,我只是想确保将来它不会影响查询性能。
  • 您关于增加运行次数实际上会增加写入次数的评论现在对我来说非常有意义。单次运行肯定会减少,但总体而言只会增加。在那种情况下,我猜 Laurenz 的回答(使用 fillfactor)可能会有所帮助。
  • 就信息而言,当人们有非常大的表需要更新数十亿行并且需要更频繁地运行真空时,他们会怎么做?实际清理桌子可能需要很多时间?一种可能是分片,以便将表/数据库拆分为多台机器,每台机器都有自己的 IO 带宽?
  • @hardik24 我认为在一个表中有数十亿行是非常罕见的,并且每天有超过 30% 的行被翻动。如果您确实遇到这种情况,您应该能够在 auto-vacs 上抛出更多的 IO,或者完全关闭 IO 限制。 v13 还将引入并行清理(仅适用于具有多个索引的表)。分片(或只是分区)也会有所帮助,但我认为这些通常会出于其他原因进行,而不仅仅是出于吸尘原因。
猜你喜欢
  • 2012-11-04
  • 1970-01-01
  • 2018-03-15
  • 1970-01-01
  • 1970-01-01
  • 2013-06-16
  • 2013-02-26
  • 2011-12-28
  • 1970-01-01
相关资源
最近更新 更多