为什么 vacuum full 在 "done" 之后等待?

Why does vacuum full wait after it is "done"?

我正在运行吸尘一个非常大的table。

当我 运行 它时,它说:

bacula=# VACUUM FULL VERBOSE file_partition_19
bacula-# ;
INFO:  vacuuming "public.file_partition_19"
INFO:  "file_partition_19": found 16242451 removable, 21024161 nonremovable row versions in 900380 pages
DETAIL:  0 dead row versions cannot be removed yet.
CPU 5.14s/14.42u sec elapsed 19.61 sec.
VACUUM
Time: 163784.767 ms
bacula=# 

执行此操作时,它会很快显示到 CPU 行,然后等待很长时间才能显示最后两行(+ 提示)。这反映在时间差异上 - "elapsed 19.61 sec",与 163 秒的 "Time:" 相比(显示是因为我设置了 \timing on)。

虽然我没有给它们计时,但两次都差不多——启动命令,等待 20 秒,然后显示到 "CPU" 行,然后等待大约 3 分钟,然后打印其余部分.

这正常吗?为什么会这样?

它主要是重建 table 上的所有索引,它必须这样做,因为基本上 "VACUUM FULL" 完全重写了 table。如果您从 table 中删除所有索引,在 "CPU" 行之后应该几乎没有延迟。

AFAICT,CPU 用法行由通用例程打印,该例程为其他 (non-FULL) 真空模式完成大部分工作。在"VACUUM FULL"的情况下是没有意义的。

如果您担心它花费的时间太长,我建议您查看 PostgreSQL wiki 中的“When to use VACUUM FULL and when not to”。当人们使用 VACUUM FULL 时,10 次中有 9 次他们实际上不应该使用。

根据您在问题中使用的标签 "postgres-9.3",我假设您拥有 Postgres 9.3 版本。

你可以参考这个 link 只是为了你自己对 Postgres 9.0 之前版本的 "VACUUM" 和 "VACUUM FULL" 的了解。

VACUUM VS VACUUM FULL For Pre-9.0 versions of Postgres

所以当你有 Postgres-9.3 时,文档说明如下:

For clarity, 9.0 changes VACUUM FULL. As covered in the documentation, the VACUUM FULL implementation has been changed to one that's similar to using CLUSTER in older versions. This gives a slightly different set of trade-offs from the older VACUUM FULL described here. While the potential to make the database slower via index bloating had been removed by this change, it's still something you may want to avoid doing, due to the locking and general performance overhead of a VACUUM FULL.

根据当前文档,VACUUM FULL 操作不仅从记录被标记为已删除的 table 中检索 space,而且还会触及 table 中的每个有效记录,并且尝试在数据库页面中重新组织它们,这样它就可以释放更多 space 然后只是 VACUUM 操作。所以在 VERBOS 结果中,当我们看到行

CPU 5.14s/14.42u sec elapsed 19.61 sec

这是系统进程遍历 table 并分析 table 并检索已标记的 space 所花费的时间。然后它开始将记录组织到页面文件中,因此根据 table 页面的碎片化程度,该过程将花费时间。

例如,如果您有一个新的 table 并不断添加新记录 incrementally/sequentially,以便新记录被添加到页面底部(基于定义的主键)。现在您以相反的顺序执行删除操作,以便仅从页面底部删除记录。假设您从 table 中删除了一半的记录。在这种情况下,没有太多的页面碎片(几乎为 0),因此当 VACUMME FULL 运行第二阶段时,它仍会尝试组织有效记录,但因为没有碎片,因此它不必实际移动任何记录并且会完成得更快。

但是,上面解释的情况并不是 update/delete 现实世界中发生的情况。 table 上的真实单词 Update/Delete 会产生大量页面碎片,因此在第二阶段 VACUUM FULL 过程实际上必须将有效记录移动到每个页面开头的空闲 space 中,因此需要更多时间.

检查以下示例输出,

I 运行 非常小的假人 table。即使它只有 7 行。 VACUME 过程 (第一阶段)在 0.03 秒(30 毫秒) 内完成,但 报告的总查询在 61 毫秒内完成 。所以这告诉我,即使没有什么可以重组的,这个过程仍然会检查它是否可以重组多少,因此需要时间。但是,如果我实际上有很多碎片并发生重组,那么完成时间会更长,具体取决于页面碎片。