Situation: A frequent read query on a large table shows Bitmap Index Scan → Bitmap Heap Scan with many random I/Os because the planner must visit heap tuples to fetch the two small columns selected.Approach: Enable index-only scans so PostgreSQL can satisfy the query from the index without touching the heap. Three levers: build a covering index with INCLUDE columns, cluster the table on that index (or use BRIN for physical locality), and ensure visibility map is healthy (VACUUM).Recommended steps and commands:1) Create a covering B-tree index that contains the WHERE columns as key columns and the two selected columns as included payload:sql
CREATE INDEX CONCURRENTLY idx_table_filter_cover
ON schema.table (filter_col1, filter_col2)
INCLUDE (small_col_a, small_col_b);
2) Force clustering (one-time) to lay out table rows by index order to make any remaining heap access more sequential:sql
CLUSTER VERBOSE schema.table USING idx_table_filter_cover;
-- or for large tables:
ALTER TABLE schema.table SET (toast.toast_tuple_target = 2048);
3) Maintain visibility map: run VACUUM (autovacuum tuning if needed):sql
VACUUM ANALYZE schema.table;
Alternative: If table is extremely large and queries touch contiguous time ranges, a BRIN index reduces random I/O:sql
CREATE INDEX idx_table_brin ON schema.table USING brin(time_col) WITH (pages_per_range = 32);
Key reasoning:- INCLUDE stores non-key columns in the index leaf pages; index-only scans are possible if visibility map marks tuples as visible.- CLUSTER arranges heap in index order, reducing random heap reads but is a one-time blocking operation; it must be re-run after substantial writes.- VACUUM sets visibility map bits so index-only scans don’t consult heap.Trade-offs:- Index bloat and extra storage for included columns; slower writes (INSERT/UPDATE/DELETE) and longer index maintenance time.- CLUSTER is heavy and requires downtime or careful scheduling; it does not stay effective under high churn and can cause fragmentation — re-cluster periodically.- Increased autovacuum load to maintain visibility map; consider autovacuum tuning (lower thresholds) for hot tables.- BRIN has tiny index size and low write cost but only helps when physical ordering correlates with query predicate.Result: A covering index + healthy visibility map allows index-only scans, removes bitmap heap random I/O, and greatly speeds frequent reads; monitor index size, autovacuum, and re-cluster frequency to balance read performance vs. write/maintenance cost.