Make index-only scans actually skip the heap
Without a current visibility map the planner often will not pick a true index-only scan at all. VACUUM sets the map, after it, the same count becomes an Index Only Scan with Heap Fetches: 0.
Problem
What you're actually looking at
The symptom as it shows up on a real server.
"Index Only Scan" is a promise, not a guarantee. For every heap page not marked all-visible in the visibility map, PostgreSQL still fetches the row to check visibility. A freshly written table has an empty map, so the planner may stick to a heap-touching plan, or an Index Only Scan that still reports heap fetches.
A Meridian count over consignments could be served entirely from an index, but right after loading the visibility map is unset, so the plan still touches the heap.
Simple terms
An index-only scan is meant to answer a query from the index alone, without ever opening the table. But PostgreSQL still has to confirm each row is visible to you, and it can only skip that check for pages the visibility map has marked all-visible. A freshly loaded table has an empty map, so you often do not get a true index-only plan yet, the scan keeps touching the heap. VACUUM is what fills in the map, run it and the scan becomes genuinely index-only.
Full runbook for this incident
- The full identify checklist, the exact signals that tell you it's this incident
- Every diagnostic query; lab output is attached only to the steps we actually captured
- The resolution path and the pitfalls that make it worse
- Mitigation steps to stop it recurring, plus a verify-you're-done query
Card required. Cancel before day 7 and you are not charged.
More in this category
Other Indexing runbooks
Neighbouring incidents that share the same diagnostic surface.
Need the full procedure?
Pro runbooks finish the incident path
Free runbooks teach the shape. Pro opens the full step transcript, edge cases, and prevention depth.