Skip to content

C08 — Visibility Map Integrity

The visibility map is an internal structure PostgreSQL uses to skip unnecessary work during vacuuming and index-only scans — when it's wrong, both get slower or, in rarer cases, return wrong answers. This category checks for VM/heap mismatches (via pg_visibility, where installed) and the specific advisory state PostgreSQL can leave behind after a crash.

6 checks, PGHF08-001 through PGHF08-006. Every check here is PGHF-namespace and built-in — see Metadata Columns Explained for what each field below actually means, and Resetting to Defaults for why these definitions can't be hand-edited in place.

PGHF08-001 — Heap block reads vs index scans

Disproportionately high heap block reads relative to index scans suggests the visibility map is too stale to support index-only scans — the planner has to visit the heap anyway to confirm visibility, wasting the whole point of an index-only plan.

How to fix

VACUUM ANALYZE schema.the_table;

VACUUM (not just ANALYZE) is what actually refreshes the visibility map. If this recurs quickly on a high-write table, the table needs more frequent autovacuum — see PGHF05-006's autovacuum_vacuum_scale_factor remediation.

PGHF08-002 — relallvisible consistency

relallvisible drifting far out of line with relpages means the catalog's visibility-map statistics are out of sync with the table's actual state — a VACUUM ANALYZE refreshes them, but until then the planner is working from stale numbers.

How to fix

VACUUM ANALYZE schema.the_table;

PGHF08-003 — Post-crash visibility map advisory

On PostgreSQL versions before 9.6, visibility map pages aren't WAL-logged, so they can be stale immediately after a crash or unclean restart — a reminder to treat VM-derived index-only-scan results with extra suspicion right after recovery.

How to fix

If this server just recovered from a crash and is still on a pre-9.6 version (itself long past end-of-life — see PGHF01-002), run VACUUM cluster-wide to rebuild visibility map state before trusting index-only scan results:

VACUUM;  -- cluster-wide, per database

The real fix is upgrading off a version that's been unsupported for years — this advisory is a stopgap, not a destination.

PGHF08-004 — pg_visibility extension

Without pg_visibility installed, there's no way to periodically run pg_check_frozen()/pg_check_visible() to catch visibility-map inconsistencies directly — the diagnostic capability PGHF08-006 depends on simply doesn't exist yet.

How to fix

CREATE EXTENSION pg_visibility;
SELECT pghf.seed_data();  -- registers PGHF08-006 now that the extension is present

PGHF08-005 — Dead tuple counter anomaly candidates

A high-write table reporting suspiciously few dead tuples despite heavy update/delete activity is a red flag that its statistics were reset (pg_stat_reset()) rather than genuinely clean — worth cross-checking against autovacuum logs before trusting the number.

How to fix

No direct fix — this is a data-quality flag on the statistics, not the table itself. Check pg_stat_user_tables.last_autovacuum/the server log for autovacuum activity around the suspected reset time, and treat any bloat-related check (PGHF05-003/PGHF05-009) on this table with extra scrutiny until stats have accumulated over a full representative period again.

PGHF08-006 — VM integrity (pg_visibility)

This is the direct, page-level check for VM/heap mismatches that autovacuum cannot self-heal — a page incorrectly marked all-visible or all-frozen despite containing problematic tuples means index-only scans can silently return wrong results.

How to fix

For a table flagged with actual VM/heap mismatches:

SELECT * FROM pg_check_frozen('schema.the_table');  -- lists specific bad tuples
VACUUM schema.the_table;  -- does NOT clear an already-wrong all-visible/all-frozen bit

A genuinely corrupted visibility map bit is not fixed by VACUUM alone — on PG9.6+, pg_visibility provides pg_truncate_visibility_map('schema.the_table') to force a full VM rebuild on the next vacuum, which is the actual repair path here. Treat repeated occurrences on the same table as a signal to also run PGHF07-007/PGHF07-009 for heap-level corruption, not just a VM-layer issue.

Continue to C09 — WAL & Replication Slot Health.