Lesson 10 · Indexes
Why indexes exist: the B-tree
The alternative to reading every page — and the one index you'll use 90% of the time.
Your win: explain what an index is, how a B-tree finds a row without a sequential scan, and what kinds of query it accelerates.
The problem: the sequential scan
Without a useful index, finding rows means a sequential scan — reading every page of the heap (Part 2) and checking each tuple. Fine for a small table or when you want most of it; disastrous for finding a handful of rows in millions.1
An index is a sorted side structure
An index is a separate structure that lets Postgres locate matching rows fast. The default type is the B-tree — a balanced search tree:
Branch nodes route a lookup down to a leaf in ~O(log n) steps; leaves hold
the indexed keys in sorted order as a doubly linked list, each pointing at a heap
tuple's ctid.2 Because the leaves are
sorted, a B-tree serves equality (=), ranges
(<, >, BETWEEN), ORDER BY, and
prefix LIKE 'abc%'.
ctid; Postgres then does a heap fetch
to read the actual row from its page (Part 2). So an index lookup is "walk the tree →
fetch the row". (An index-only scan can skip the fetch — Lesson 12.)
pk__emails
on email_id. Your migrations also add explicit B-trees on
resource_path (tom/1059:43) and composites like
(email_id, recipient_address, recipient_type)
(notificationmgmt/1026). B-tree is the workhorse.
Use The Index, Luke! — Anatomy of an index
The clearest explanation of the B-tree (the tree + the doubly-linked leaf nodes) — the single best resource for this whole part. Then the official indexes chapter.
→ use-the-index-luke.com/sql/anatomy
→ postgresql.org/docs — Indexes
Check yourself (from memory)
Q1. Without a useful index, a query does a…
Q2. The default Postgres index type is…
Q3. A B-tree efficiently supports…
=, ranges,
ORDER BY, prefix LIKE. Multi-value data needs GIN (Lesson 11).
ctid) to heap tuples. Lookup is ~O(log n); sorted leaves serve equality
(=), ranges (</>/BETWEEN),
ORDER BY, and prefix LIKE. After finding the ctid it does a heap
fetch (unless index-only). Every PK/unique is a B-tree.EXPLAIN flip from "Seq Scan" to "Index Scan" when you add one?
That's Part 4 — ask me for a preview.