新建会话
  • PPT
  • 网站
  • 文档
  • 深度研究
  • 表格
  • Agent 集群
Kimi Code
Kimi Claw
历史会话
  • 获取应用程序
  • 关于我们
    • 探索月之暗面
    • 开放平台
    • 社会招聘
    • 校园招聘
    • 用户协议
    • 隐私协议
  • Language
  • 用户反馈

Vacuum前后差异

Group (cost=0.43..600032.52 rows=107 width=22) (actual time=0.019..6437.286 rows=107.00 loops=1) Output: content Group Key: test1.content Buffers: shared hit=6660031 -> Index Only Scan using test_idx on test.test1 (cost=0.43..572532.52 rows=11000001 width=22) (actual time=0.018..5664.125 rows=11000001.00 loops=1) Output: content Heap Fetches: 11000001 Index Searches: 1 Buffers: shared hit=6660031 Planning Time: 0.073 ms Execution Time: 6437.376 ms 上面是vacuum full后的执行计划,下面是vacuum full前的执行计划 Group (cost=0.43..209778.68 rows=107 width=22) (actual time=0.055..1434.377 rows=107.00 loops=1) Output: content Group Key: test1.content Buffers: shared hit=321 read=8628 -> Index Only Scan using test_idx on test.test1 (cost=0.43..184779.50 rows=9999671 width=22) (actual time=0.054..810.491 rows=10000000.00 loops=1) Output: content Heap Fetches: 0 Index Searches: 1 Buffers: shared hit=321 read=8628 Planning: Buffers: shared hit=24 Planning Time: 0.129 ms Execution Time: 1434.431 ms 为何会有如此差异
你观察到的两个执行计划差异非常典型,VACUUM FULL 前后的性能差异主要源于数据物理存储方式的变化,尤其是 索引可见性(visibility map) 和 堆页(heap page)的清理状态。

✅ 关键差异点对比

表格
指标VACUUM FULL 前VACUUM FULL 后
Heap Fetches011,000,001
Buffers (hit)3216,660,031
Execution Time1.4s6.4s
Rows Scanned10M11M

🔍 根本原因分析

✅ VACUUM FULL 前:Heap Fetches = 0

  • 说明 Index Only Scan 完全不需要访问堆页,因为 visibility map 标记所有元组为可见。
  • 这是 最优的 Index Only Scan 行为,性能极高。
  • 数据页和索引页都 没有被清理或重写,但 visibility map 是最新的。

❌ VACUUM FULL 后:Heap Fetches = 11,000,001

  • 说明 每扫描一个索引项,都要回表一次,即 访问堆页确认可见性。
  • 这是因为 VACUUM FULL 会重写整个表,导致:
    • 所有元组的 TID(行物理位置)发生变化;
    • visibility map 被清空或失效;
    • 索引未被重建,但指向的堆页位置已变;
    • 因此 Postgres 无法信任 visibility map,必须回表确认每一行是否可见。

🧪 总结一句话

VACUUM FULL 后,visibility map 失效,导致 Index Only Scan 退化成了“Index Scan + Heap Fetch”,性能暴跌。

✅ 如何验证

你可以运行以下 SQL 来确认 visibility map 状态:
sql
复制
SELECT pg_visibility_map('test.test1'::regclass, 1);
或者查看:
sql
复制
SELECT relallvisible FROM pg_class WHERE relname = 'test1';
  • relallvisible 在 VACUUM FULL 后通常会变成 0;
  • 在 VACUUM(非 FULL)后,这个值应该接近实际页数。

✅ 如何修复

不需要再次 VACUUM FULL,只需:
sql
复制
VACUUM ANALYZE test.test1;
这会让 PostgreSQL 重新构建 visibility map,恢复 Heap Fetches = 0 的高效状态。

✅ 建议

  • 避免在生产环境频繁使用 VACUUM FULL,除非你真的需要回收磁盘空间;
  • 日常维护用 VACUUM + ANALYZE 即可;
  • 如果你需要重建表(如膨胀严重),考虑使用 pg_repack 或 CLUSTER,它们对 visibility map 更友好。

如你愿意,我可以帮你写一个脚本,自动判断是否需要 VACUUM 或重建表。