Question: Indexes
Why is my index bigger than my table?
Answered in the first paragraph. Last updated .
Usually because it is holding entries for row versions the table no longer counts. There are three explanations and they are easy to tell apart. The index is bloated by updates, which is the common one. The indexed value is large relative to the rest of the row, which is arithmetic rather than a problem. Or the index has several columns on a narrow table, in which case being larger is simply correct.
Why updates inflate an index
Every new version of a row normally needs new index entries, even when the indexed columns did not change. There is a cheaper path that avoids touching the indexes, and it is only available when the new version fits on the same page as the old, which is what HOT updates and fillfactor is about. A table with no spare room on its pages loses that path and starts writing index entries at the rate it writes rows.
The entries left behind by old versions are removed lazily, and a page that empties is available for reuse by that index but not returned to the filesystem. So an index that took a burst of updates keeps its size afterwards even though the live entry count is unchanged, which is bloat in its most visible form.
Insertion order contributes too. Keys arriving in random order split pages and leave both halves partly full, where a monotonically increasing key packs pages tightly.
Rebuilding it, and stopping it coming back
Rebuild concurrently so the table stays writable. Two things to know about that: it cannot run inside a transaction block, and a failed run leaves a transient invalid index behind with a recognisable name suffix that has to be dropped before trying again. The same failure mode applies to building a new index, covered in is CREATE INDEX CONCURRENTLY safe.
Then remove the cause. Leaving free space on pages restores the cheap update path for a table that is updated in place. Reducing the number of indexes on a hot table helps more than any setting, because each one is another write per row version and another rebuild later.
And check that the index is earning its keep at all before deciding to rebuild it, since an index nothing reads is pure cost whatever its size. Unused and missing indexes covers proving that. If it is large because the values themselves are large, the ceiling on a single entry is worth knowing before the next one fails outright, which is index row size exceeds the btree maximum.