Nikolay Samokhvalov:转储/恢复完成了,接下来呢?

💡 原文英文,约18500词,阅读约需68分钟。
📝

内容提要

本文介绍一组PostgreSQL恢复测试脚本,核心结论是pg_dump/pg_restore后数据库并未就绪,需手动维护。测试比较了VACUUM、VACUUM FREEZE、ANALYZE等选项的耗时与WAL开销,并验证autovacuum能否自动补做。分区父表的统计信息需单独执行ANALYZE,vacuumdb --analyze会跳过父表。调查28篇文档,仅8篇建议ANALYZE、3篇建议VACUUM、1篇解释可见性映射,说明官方指南普遍缺失这一关键步骤。

🔎

延伸解读

逻辑恢复后为何仍需维护

pg_dump/pg_restore 只复制数据行,不携带列统计信息和可见性映射。缺少统计信息时,查询规划器会使用默认估算,可能选择低效的执行计划;可见性映射为空则导致 Index Only Scan 必须回表读取堆数据。文章测试中,同一查询在 pg_restore 后耗时 32.6 秒,执行 VACUUM ANALYZE 后降至 23 毫秒。因此,恢复完成不等于数据库就绪,需手动运行维护命令。

ANALYZE 与 VACUUM 的分工

ANALYZE 收集规划器统计信息,修正行数估算,从而改善连接顺序和扫描方式;VACUUM 构建可见性映射,使 Index Only Scan 能跳过堆访问。测试显示,仅 ANALYZE 可将查询从 32.6 秒降至 6.1 秒,但仍有约 2.5 万次堆读取;追加 VACUUM 后堆读取降为 0,查询进一步降至 23 毫秒。两者作用不同,不可互相替代。

分区父表统计需单独处理

autovacuum 会处理每个叶子分区,但不会为分区父表收集统计信息。文章测试中,8 个分区在 15 秒内被自动分析,而父表在 480 秒后仍无统计信息,导致父表查询使用 403,072 个缓冲区,手动 ANALYZE 后降至 4,400。此外,PostgreSQL 14–18 的 vacuumdb 自动选择表时会跳过分区父表;19 的 --analyze 仍会跳过,需使用 --analyze-only 或手动对父表执行 ANALYZE。

PostgreSQL 19 的新写入效应

在 PostgreSQL 19 中,普通读取可设置可见性映射位,但这会将只读查询变为写入操作。测试中,pg_restore 后首次 SELECT 耗时 110–113 秒,并生成约 31 GiB WAL,因为该查询标记了几乎全部堆页面。若在流量到来前执行 VACUUM ANALYZE,可避免这种首次读取开销。文章建议在 19 上于首次查询前完成维护,而非之后。

❓

Q&A

pg_dump/pg_restore 完成后,为什么数据库还不能直接上线?

因为逻辑恢复只复制了数据行,没有复制列统计信息和可见性映射。缺少统计信息会导致查询计划变差,缺少可见性映射会使 Index Only Scan 需要回表读取堆数据,从而拖慢查询。

恢复后应该运行哪些维护命令?顺序是什么?

应先运行 ANALYZE 收集统计信息,再运行 VACUUM 构建可见性映射。对于分区父表,还需要单独执行 ANALYZE。可选地使用 VACUUM (FREEZE, ANALYZE) 来避免后续堆重写。

VACUUM 和 ANALYZE 分别解决什么问题?为什么两者都需要?

ANALYZE 收集列统计信息,帮助规划器选择正确的连接方式和索引;VACUUM 构建可见性映射,使 Index Only Scan 无需回表。两者结合才能让查询性能恢复到正常水平。

autovacuum 能自动完成恢复后的维护吗?有什么局限?

autovacuum 可以自动对叶子分区执行 VACUUM 和 ANALYZE,但不会为分区父表收集统计信息。此外,在繁忙集群中 autovacuum 可能延迟数分钟才处理表,因此不能依赖它及时完成维护。

PostgreSQL 19 在恢复后首次查询时有什么特殊行为?

PostgreSQL 19 允许普通读取设置可见性映射位,因此首次 SELECT 可能写入大量 WAL 并显著变慢。例如测试中首次查询耗时 110-113 秒,并生成约 31 GiB WAL。建议在首次查询前手动运行 VACUUM ANALYZE。

官方文档对恢复后运行 ANALYZE 和 VACUUM 的建议充分吗?

不充分。调查的 28 篇指南中,仅 8 篇建议运行 ANALYZE,3 篇建议运行 VACUUM,只有 1 篇解释了可见性映射。pg_restore 本身也不输出任何建议,因此这一关键步骤常被忽略。

🏷️

标签

➡️

继续阅读