Overview
A B-tree index keeps keys in sorted order in a balanced tree whose leaf pages point to heap rows. It serves =, ranges, ORDER BY (no sort node), and IS NULL on any sortable type. Default to it for columns in WHERE, JOIN, or ORDER BY, and use gin-index, GiST, or BRIN only when the query shape demands it (full-text, JSONB, arrays, geometry, huge append-only tables).
Rules
- Order composite columns equality first, range or sort last:
(user_id, created_at DESC). PostgreSQL 18 can skip-scan a multicolumn index when leading columns have few distinct values; do not rely on it over correct ordering. LIKE 'prefix%'uses a B-tree only under the C locale or with atext_pattern_opsindex; keep a default-opclass index too if you also need<or>comparisons.- Use partial indexes (
WHERE status = 'pending') and covering indexes (INCLUDE, enabling Index Only Scans) to stay small. - Build on live tables with
CREATE INDEX CONCURRENTLY; rebuild bloated ones withREINDEX CONCURRENTLY. - Every index slows writes (secondary-index).
CREATE INDEX idx_orders_user_date ON orders (user_id, created_at DESC);
CREATE INDEX idx_orders_pending ON orders (created_at) WHERE status = 'pending';
CREATE INDEX idx_orders_covering ON orders (user_id) INCLUDE (status, total_cents);Related
- query-plan: confirm the planner uses the index.
- gin-index: the index for composite values.
- secondary-index: the write cost of every index.
- postgres-indexes: index strategy in depth.
- postgres-explain: reading plans for index use.