Cartwheel's orders table carries nine indexes and three of them have never been scanned. Six topics take the index set apart on the real schema: what a B-tree costs on every write, how column order and INCLUDE decide whether a query touches the table, which access method implements which operator, and how to build and drop indexes on a live table without stopping checkout.
6 topics
Chapter 7 left the cleanup machinery understood: dead row versions, autovacuum, and the freezing that keeps transaction ids from running out. Indexes are the other thing that machinery has to keep in step. An index entry names a physical row version, so every superseded version leaves entries behind, and every index on a table is more work for the vacuum that has to remove them. That connection runs the wrong way round in most people's heads — an index is filed under reads, and half of its cost is on the write path.
The evidence is on orders right now. Nine indexes, added one at a time across four years, each one reasonable on the day it appeared. Three have not been chosen by a plan since the statistics were last reset. The customer-history screen has an index that finds the right twenty rows and then reads the table for every one of them, because nothing in its definition says which columns the screen displays. On delivery_events, a 40 GB B-tree on occurred_at does a job that 90 MB of a different index type would do better. And the product search runs ILIKE '%straw%' against 200,000 rows on every keystroke, which no B-tree can help with at all.
Each topic fixes one of those, on the real schema, with the numbers attached. What this chapter deliberately does not do is teach you to read a plan — that is Chapter 9's subject, and it arrives immediately afterwards for a reason. An index is a bet about how the planner will price a query, and the chapter after this one is where you learn to check whether the bet paid.
An index is filed under reads. Half of its cost is on the write path.
Nine indexes on ordersadded one at a time across four years
→
Every write maintains all ninethe row itself, then one entry per index
→
Every superseded version leaves nine behindone dead entry per index
→
Vacuum removes thema full pass over every index, every run
→
Three return nothingnot chosen by a plan since the statistics were reset