【问题标题】:Postgres auto-vacuum doesn't reclaim the dead tuples space causes disk full issuePostgres auto-vacuum 不会回收死元组空间导致磁盘已满问题
【发布时间】:2020-08-11 15:38:50
【问题描述】:

我有一个用例,每分钟同时在另一端插入 100 000 行,少数线程会获取这些行并将它们从我的表中删除。所以肯定会在我的表中创建很多死元组。

我的自动真空配置是

autovacuum_max_workers = 3
autovacuum_naptime = 1min
utovacuum_vacuum_scale_factor = 0.2
autovacuum_analyze_scale_factor = 0.1
autovacuum_vacuum_cost_delay = 20ms
autovacuum_vacuum_cost_limit = -1

从“pg_stat_user_tables”中,我可以发现我的表上正在运行 auto-vacuum,但在几个小时内我的磁盘将满 (500 GB),我无法插入任何新行。

在第二次尝试时,我更改了以下配置

autovacuum_naptime = 60min
autovacuum_vacuum_cost_delay = 0

这次我的模拟和自动真空运行良好,最大磁盘大小为 180 GB。

这里我的疑问是,如果我将“autovacuum_vacuum_cost_delay”更改为零毫秒,自动真空如何释放死元组空间并 PG 重用它?如果我将值设置为 20 毫秒,为什么它没有按预期工作?

【问题讨论】:

  • “每分钟缺少 1 行”是什么意思?您是否每分钟仅插入和删除 1 行?行有多大?
  • 另外,如果你遇到这样的问题,那么你应该减少午睡时间,而不是增加到 60 分钟。
  • @JonathanJacobson 我认为他的意思是 10 万行,即 100,000 行。 en.wikipedia.org/wiki/Lakh
  • 谢谢@jjanes - 我不知道这个词

标签: postgresql postgresql-9.6 autovacuum


【解决方案1】:

这里我的疑问是,如果我将“autovacuum_vacuum_cost_delay”更改为零毫秒,自动真空如何释放死元组空间并 PG 重用它?

vacuum 释放的空间记录在free space map 中,从那里分发以供将来的 INSERT 重复使用。

要补充的另一个细节是,在 9.6 中,只有在整个表本身被完全清空后才会清空可用空间映射,因此在此之前无法找到释放的空间。如果 VACUUM 由于太慢或被中断而永远无法完成,那么它释放的空间将不会被重新用于 INSERT。这在 v11 中得到了改进。

如果我将值设置为 20 毫秒,为什么它不能按预期工作?

因为真空无法跟上该值。 PostgreSQL 的默认值通常只适用于较小的服务器,而您的服务器似乎并不适用。在这种情况下更改默认值是适当且可取的。请注意,在 v12 中,默认值从 20 降低到 2(其类​​型也相应地从 int 更改为 float,因此您现在可以更精确地指定该值)

【讨论】:

  • 谢谢@jjanes ...对自动真空还有一个疑问..假设一个自动真空线程在完成任务之前是否在我的桌子上运行,如果另一个自动真空线程即将到来并且清理同一张表,那么第一个线程会发生什么?第一个线程将被暂停或中断或只是中断他的工作?
  • 第二个会跳过表格。第一个将继续。
  • 如果它是这样工作的,那么第一个线程应该在某个时间点完成他的真空任务并释放空间对吗?除非第一个线程有任何异常?
【解决方案2】:

总而言之,您的应用会创建大量死元组,而 autovacuum 无法跟上。可能的解决方案

  1. 这听起来更像是一个任务队列,而不是一个常规的表。也许 PostgreSQL 表不适合您的这个特定用例。请改用 RabbitMQ/Redis 等解决方案。
  2. 创建基于时间的范围分区并在旧分区为空时清除它们,同时仅在此表上禁用自动清理。如果可以识别已处理的分区,请考虑根本不删除行,而只清除旧分区。
  3. 调整 autovacuum 设置,使其能够持续工作,没有任何小睡或干扰。增加 maintenance_work_mem 也可以帮助加快 autovacuum。也许您会发现您已经达到了硬盘驱动器的极限。在这种情况下,您必须优化存储,以便它能够容纳那些昂贵的INSERT+DELETE+autovacuum 操作。

【讨论】:

  • 谢谢@Jonathan Jacobson。我对 RabbitMQ 了解不多……但关于 Redis,它不适合我们的用例,因为对于我的用例,每行大小为 0.1Mb。由于 Redis 使用 RAM,因此在 Redis 中存储大量数据会非常昂贵,但无论如何,如果在删除线程时发生任何错误,我们仍将删除数据,组件应该至少保存数据几个小时,对吧?
  • @Mideenabdulgaffoor 每小时创建一个分区,并定期清除旧分区。 0 个已删除的元组。 0 为 autovacuum 工作。
【解决方案3】:

默认值是2 msAutovacuum。所以你的20ms 值很高:

autovacuum_vacuum_cost_delay(浮点数)

"指定将在自动VACUUM操作中使用的成本延迟值。如果指定-1,将使用常规vacuum_cost_delay值。如果指定此值不带单位,则以毫秒为单位。默认值为2 毫秒。该参数只能在 postgresql.conf 文件或服务器命令行中设置;但可以通过更改表存储参数来覆盖单个表的设置。"

正如这里所解释的Vacuum

" Vacuum_cost_delay (浮点数)

超过成本限制时进程休眠的时间量。如果指定此值没有单位,则以毫秒为单位。默认值为零,这将禁用基于成本的真空延迟功能。正值可实现基于成本的吸尘。

使用基于成本的清理时,vacuum_cost_delay 的适当值通常非常小,可能小于 1 毫秒。虽然 vacuum_cost_delay 可以设置为毫秒值,但在旧平台上可能无法准确测量此类延迟。在这样的平台上,将 VACUUM 的节流资源消耗增加到超过 1 毫秒时将需要更改其他真空成本参数。尽管如此,您应该将vacuum_cost_delay 保持在您的平台持续测量的范围内;大的延迟是没有帮助的。 "

【讨论】:

  • autovacuum_vacuum_cost_delay 的默认值在 9.6 中是 20 毫秒,这是 OP 正在使用的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-06-08
  • 1970-01-01
  • 2021-04-01
  • 1970-01-01
相关资源
最近更新 更多