PostgreSQL Index Size Deep Dive: Why Indexes Grow Fast
PostgreSQL index size surprises many teams in production. Indexes often grow close to table size, or larger, especially on write-heavy systems.
This article explains the main causes and practical actions to reduce index growth.
1. Why PostgreSQL Indexes Get Large
Main reasons:
- multiple indexes on overlapping columns
- high update rate creating dead tuples
- low-selectivity indexes with little query benefit
- wide index keys (long text, composite keys)
- missing maintenance (
VACUUM,REINDEXwhen needed)
2. MVCC Impact on Index Growth
PostgreSQL uses MVCC. Updates create new row versions. Old versions remain until cleanup, and index entries also reflect this churn.
Implications:
- write-heavy tables can accumulate index bloat quickly
- autovacuum tuning directly affects index growth behavior
3. Check Which Indexes Are Worth Keeping
Common anti-pattern: adding indexes for occasional queries, then never reviewing usage.
Find low-usage indexes:
SELECT
schemaname,
relname AS table_name,
indexrelname AS index_name,
idx_scan,
pg_size_pretty(pg_relation_size(indexrelid)) AS index_size
FROM pg_stat_user_indexes
ORDER BY idx_scan ASC, pg_relation_size(indexrelid) DESC;
Large indexes with near-zero scans are strong cleanup candidates.
4. Detect Index Bloat Signals
Useful indicators:
- table write volume high, index size growth outpaces table growth
- query latency does not improve despite additional indexes
- autovacuum runs frequently but bloat still increases
Track index growth trend weekly per table, not only point-in-time size.
5. Design Better Indexes
Guidelines:
- index only columns used by frequent filters, joins, and ordering
- prefer narrow keys
- avoid redundant prefix-overlap indexes
- use partial indexes for filtered workloads
Example partial index:
CREATE INDEX CONCURRENTLY idx_orders_open_created_at
ON orders (created_at)
WHERE status = 'OPEN';
This is often smaller and faster than a full-table index.
6. Composite Index Order Matters
For (a, b, c), PostgreSQL can use left-prefix access efficiently. If most queries filter by b only, this index may not help.
Choose column order based on real query predicates and cardinality.
7. Maintenance Strategy
Baseline:
- ensure autovacuum settings fit write volume
- use
REINDEX CONCURRENTLYfor heavily bloated critical indexes - run periodic index review as part of DBA operations
Example:
REINDEX INDEX CONCURRENTLY idx_orders_open_created_at;
8. Practical Optimization Workflow
- Identify top tables by index storage.
- List indexes by size and usage.
- Remove redundant or unused indexes safely.
- Replace broad indexes with targeted partial/composite indexes.
- Tune autovacuum and monitor growth trend.
9. Production Checklist
- Every index has a known query use case.
- No large unused indexes in
pg_stat_user_indexes. - Autovacuum tuned for high-write tables.
- Index growth reviewed regularly with alerts.
Index size is a performance budget decision. Keep only indexes that clearly pay for their write and storage cost.