如何查找经VACUUM(FULL, ANALYZE)处理但无VERBOSE输出的表
你遇到的last_vacuum字段不更新是PostgreSQL的长期已知问题:VACUUM FULL本质是表重写操作,没有走常规VACUUM的统计上报逻辑,因此无法直接通过pg_stat_user_tables的last_vacuum字段判断处理状态,不需要依赖VERBOSE日志,用以下几个方法交叉验证即可准确筛选已处理/未处理表:
方法1:物理文件修改时间校验(准确率最高)
VACUUM FULL执行完成后会为表生成全新的物理数据文件,文件最后修改时间就是操作完成时间,这个逻辑不受统计bug影响,是最可靠的判断依据。
使用超级用户执行以下SQL,可以查询所有用户表对应数据文件的修改时间:
SELECT n.nspname AS schema_name, c.relname AS table_name, (pg_stat_file(pg_relation_filepath(c.oid))->>'modification')::timestamptz AS file_modify_time, s.last_analyze, s.n_dead_tup FROM pg_class c JOIN pg_namespace n ON c.relnamespace = n.oid LEFT JOIN pg_stat_user_tables s ON c.oid = s.relid WHERE c.relkind = 'r' AND n.nspname NOT IN ('pg_catalog', 'information_schema') -- 若记得全局VACUUM FULL的启动时间,可放开下面的条件直接筛选时间窗口内完成重写的表 -- AND (pg_stat_file(pg_relation_filepath(c.oid))->>'modification')::timestamptz >= '2024-XX-XX XX:XX:XX' ORDER BY file_modify_time DESC;
注意:TRUNCATE、触发表重写的ALTER TABLE、CLUSTER等操作也会更新表数据文件的修改时间,如果在维护窗口内执行过这类操作,需要手动排除对应表。
方法2:统计字段辅助校验
该已知问题仅影响last_vacuum字段的更新,VACUUM (FULL, ANALYZE)附带的ANALYZE操作会正常上报统计信息,可以作为辅助判断维度,符合以下特征的表大概率已经完成处理:
last_analyze时间落在全局VACUUM FULL的执行时间窗口内- 表的
n_dead_tup(死元组数量)接近0 analyze_count计数比维护操作前的基准值高1
注意:如果库开启了自动ANALYZE,业务高频写入的表可能在操作中断后被自动ANALYZE更新过last_analyze字段,该方法不能单独作为判断依据,必须和方法1的结果交叉验证。
方法3:表膨胀率二次校验
执行VACUUM FULL的核心效果是清理表空间膨胀,已经完成处理的表膨胀率会接近0,可以通过估算膨胀率反向筛查未处理的表,执行以下SQL即可估算用户表膨胀率:
SELECT n.nspname AS schema_name, c.relname AS table_name, ROUND( 100 * (pg_relation_size(c.oid) - (s.n_live_tup * s.avg_row_width))::numeric / NULLIF(pg_relation_size(c.oid), 0), 2) AS bloat_percent FROM pg_class c JOIN pg_namespace n ON c.relnamespace = n.oid JOIN pg_stat_user_tables s ON c.oid = s.relid WHERE c.relkind = 'r' AND n.nspname NOT IN ('pg_catalog', 'information_schema') AND pg_relation_size(c.oid) > 10 * 1024 * 1024 -- 过滤小于10MB的小表,减少无意义结果 ORDER BY bloat_percent DESC;
注意:该SQL的膨胀率是基于统计信息估算的,若表设置了较小的填充因子、或存在大量长事务占用的死元组,结果会存在误差,仅作为二次校验使用。
后续逐表补做维护时,建议先创建一张简单的维护进度记录表,每完成一张表的VACUUM FULL就写入一条完成记录,避免中断后再次需要反向排查。
内容的提问来源于stack exchange,提问作者mckenzm

