Concept lesson

B-Tree, BRIN, GIN & GiST Indexes

Comparative indexing strategies, B-Tree layout, BRIN range indexes for timeseries, and GIN inverted indexes.

lesson
Freshness: current15 min read
Mastery
not started · 0%

Learning outcomes

  • Select optimal index types based on column selectivity
  • Optimize timeseries and document queries with BRIN and GIN

Mental model

Indexes accelerate data retrieval by organizing column keys into specialized disk data structures. Choosing the wrong index type degrades write performance without improving query speeds.

Query Predicate Filter
Evaluate Selectivity & Data Type
Select Index (B-Tree / BRIN / GIN)
Index Scan Page Lookup
Fetch Matching Heap Tuples
Conceptual teaching model synthesized from:PostgreSQL 16 Architecture, MVCC & Query Optimization Manual

Theory

  • B-Tree: Default self-balancing search tree. Best for high-selectivity equality (=) and range queries (<, >).
  • BRIN (Block Range Index): Stores min/max values for block ranges (e.g. 128 pages). Extremely compact for naturally ordered timeseries data.
  • GIN (Generalized Inverted Index): Inverted index mapping elements to tuple IDs. Essential for JSONB keys, array containment (@>), and full-text search.
-- High-selectivity unique lookup (B-Tree)
CREATE INDEX idx_users_email ON users USING btree(email);

-- Compact timeseries range index (BRIN)
CREATE INDEX idx_logs_created_at ON audit_logs USING brin(created_at);

-- Inverted document index for JSONB containment (GIN)
CREATE INDEX idx_events_payload ON events USING gin(payload jsonb_path_ops);

Alternatives and trade-offs

  • B-Tree: Fast O(log N) lookups, but large disk footprint (~MBs/GBs per index).
  • BRIN: Tiny disk footprint (less than 1% of B-Tree size), but only effective when physical heap data is strictly ordered by insertion time.
  • GIN: Fast multi-element containment search, but updates incur high write amplification.

Failure modes and misconceptions

  1. Indexing Low-Cardinality Columns: Creating a B-Tree index on a boolean is_active column is ignored by the query planner because sequential scans are faster.
  2. Unused Index Overhead: Every index must be updated during INSERT, UPDATE, and DELETE operations, degrading write throughput.
Reflect before revealing the guide

Decision scenario

Use BRIN indexes on large append-only timestamp log tables (>100 million rows) to save 99% of index disk space while enabling fast range scans.

Learning outcomes

  • Evaluate column selectivity and query patterns to select the optimal index type.
  • Implement BRIN indexes for ordered append-only timeseries data.
  • Build GIN expression indexes for fast JSONB document lookups.

Trade-offs

Indexes dramatically accelerate query reads, but introduce storage overhead and slow down database write operations (INSERT/UPDATE/DELETE).

Evidence assessment

Theory and decision mastery

not-started · 0%
theory0%
decision0%
activityNot mapped
projectNot mapped
1. How does a Block Range Index (BRIN) achieve an extremely small disk footprint?
2. Which query pattern is best accelerated by a GIN (Generalized Inverted Index)?
3. Why does the PostgreSQL query planner ignore B-Tree indexes on low-cardinality columns (e.g. boolean is_active)?

Decision scenario

You are designing an event logging system storing 200 million append-only rows sorted by insertion timestamp. Disk space usage is a critical constraint.

Which index strategy optimizes range queries on timestamp while minimizing disk consumption?

Primary sources