Overview

A secondary index maps values of non-primary-key columns to row locations so the planner can satisfy WHERE, JOIN, and ORDER BY without scanning the table. Every index speeds some reads and slows each INSERT, UPDATE, and DELETE that touches its columns, so add one only for a query you have measured.

Rules

  • Pick the type by operator: btree-index (default: equality, range, sort), gin-index (arrays, full-text, JSONB), GiST (geometry, nearest-neighbor), BRIN (huge, naturally ordered tables), Hash (equality only).
  • Confirm use with EXPLAIN (ANALYZE, BUFFERS). Drop indexes whose pg_stat_user_indexes.idx_scan stays at zero across a full business cycle.
  • Index foreign-key columns on the child table; PostgreSQL does not do it for you (foreign-key).
  • Partial (WHERE active) and expression (lower(email)) indexes cover narrow access patterns at smaller size. Updates that change no indexed column can be HOT updates that skip index maintenance (write-amplification).
CREATE INDEX users_email_active_idx ON users (email) WHERE active = true;