PostgreSQL VACUUM仅扫描少量页面的原因咨询
大表VACUUM仅扫描少量页面的原因
操作场景与日志
我尝试对一个大小为790 GB的表执行VACUUM操作,执行语句及输出如下:
wento=# VACUUM (VERBOSE, ANALYZE) public.wento; INFO: vacuuming "wento.public.wento" INFO: finished vacuuming "wento.public.wento": index scans: 0 pages: 0 removed, 19249 remain, 727 scanned (3.78% of total) tuples: 0 removed, 2543622 remain, 5034 are dead but not yet removable removable cutoff: 2797274, which was 18769 XIDs old when operation ended index scan bypassed: 160 pages from table (0.83% of total) have 638 dead item identifiers avg read rate: 98.195 MB/s, avg write rate: 23.735 MB/s buffer usage: 622 hits, 724 misses, 175 dirtied WAL usage: 175 records, 175 full page images, 1372087 bytes system usage: CPU: user: 0.00 s, system: 0.01 s, elapsed: 0.05 s INFO: vacuuming "wento.pg_toast.pg_toast_19276" ^C Cancel request sent ERROR: canceling statement due to user request CONTEXT: while scanning block 102413194 of relation "pg_toast.pg_toast_19276"
我的疑问:我理解由于存在长事务,死元组未被删除,但想了解为何仅扫描了727个页面(占总页面的3.78%)?
原因分析
PostgreSQL的常规VACUUM(非VACUUM FULL)是增量式、低干扰的清理操作,仅扫描必要页面,核心原因如下:
- 仅追踪并扫描含死元组的页面:PostgreSQL会维护表中存在死元组的页面列表,VACUUM优先处理这些页面(日志中提到的160个含死元组的页面),同时会扫描少量相邻页面确保清理覆盖,这就是你看到的727个扫描页面的来源。
- 长事务导致清理条件不满足时跳过页面:由于存在未结束的长事务,死元组的移除截止XID(
removable cutoff)比长事务的XID更晚,死元组暂时无法被删除。VACUUM会识别这种情况,跳过那些即使扫描也无法完成清理的页面,避免无效的全表扫描。 - 短时间内仅完成部分扫描:本次主表的VACUUM仅耗时0.05秒,常规VACUUM会分批、低优先级地处理页面,不会一次性扫描整个790GB的大表,在短时间内仅完成了预设的少量页面扫描就进入了toast表的处理阶段。
如果需要强制全表扫描清理,可使用VACUUM (FULL, VERBOSE, ANALYZE),但注意该操作会锁表,且需要额外的磁盘空间,对大表执行风险极高,需避开业务高峰。
内容的提问来源于stack exchange,提问作者CBat
相关产品推荐
相关产品推荐

