The PostgreSQL administration field guideField notes · Runbooks · Free certification
practitioner17 minute lesson

Indexes as a workload trade-off

Evaluate indexes as structures that exchange storage and write work for selective access paths.

Progress stays on this device

Mental model

An index is not a universal speed switch. It is an ordered access structure that can avoid scanning unrelated rows when predicates and ordering align with it. Every index also consumes storage, cache, vacuum work, and write amplification. The right question is which workload becomes cheaper overall.

What healthy usually looks like

  • Important query plans use selective, well-matched indexes where they reduce total work.
  • Duplicate and genuinely unused indexes are reviewed across a representative statistics window and all replicas.
  • Index growth, write cost, and vacuum impact remain proportionate to their read benefit.

Ask the database a bounded question

Diagnostic query

Review index scans and size

Find large indexes with few recorded scans so they can be investigated against the workload and statistics window.

SELECT
  schemaname,
  relname AS table_name,
  indexrelname AS index_name,
  idx_scan,
  pg_size_pretty(pg_relation_size(indexrelid)) AS index_size
FROM pg_stat_user_indexes
ORDER BY idx_scan ASC, pg_relation_size(indexrelid) DESC
LIMIT 40;
low observation cost

Read the output

  1. Start from a costly workload, then test whether an index changes its plan and total cost safely.
  2. Design multicolumn indexes around real predicates and ordering rather than adding every referenced column.
  3. Create production indexes with the lock and resource implications understood; `CONCURRENTLY` reduces blocking but costs more work and has failure states to clean up.

Common traps

  • Dropping an index solely because `idx_scan` is zero on the primary.
  • Adding overlapping indexes without checking write amplification and cache pressure.
  • Using `EXPLAIN ANALYZE` on a write statement without accounting for the fact that it executes the statement.

Check your reasoning

What must you establish before dropping an index with zero recorded scans?

Related runbooks

Sources