What index size means
An index is a separate PostgreSQL relation that consumes storage and must be maintained as indexed data changes. Measuring index bytes helps explain a table's total footprint and identifies candidates for deeper workload review.
A large index can still be essential. Size says nothing by itself about query latency, selectivity, cache behavior, constraint ownership, or the business importance of the queries it supports.
Compare table and attached-index footprint
SELECT
n.nspname AS schema_name,
c.relname AS table_name,
pg_table_size(c.oid) AS table_bytes,
pg_indexes_size(c.oid) AS index_bytes,
pg_total_relation_size(c.oid) AS total_bytes,
pg_size_pretty(pg_indexes_size(c.oid)) AS index_size
FROM pg_class AS c
JOIN pg_namespace AS n ON n.oid = c.relnamespace
WHERE c.relkind = 'r'
AND n.nspname NOT IN ('pg_catalog', 'information_schema')
AND n.nspname !~ '^pg_toast'
ORDER BY index_bytes DESC
LIMIT 25;pg_indexes_size sums the indexes attached to the table. pg_table_size excludes indexes but includes the table's TOAST storage and auxiliary forks. pg_total_relation_size combines both.
Find the largest individual indexes
SELECT
n.nspname AS schema_name,
t.relname AS table_name,
i.relname AS index_name,
pg_relation_size(i.oid) AS index_bytes,
pg_size_pretty(pg_relation_size(i.oid)) AS index_size
FROM pg_index AS x
JOIN pg_class AS i ON i.oid = x.indexrelid
JOIN pg_class AS t ON t.oid = x.indrelid
JOIN pg_namespace AS n ON n.oid = t.relnamespace
WHERE n.nspname NOT IN ('pg_catalog', 'information_schema')
AND n.nspname !~ '^pg_toast'
ORDER BY index_bytes DESC
LIMIT 25;Add cumulative usage evidence
pg_stat_user_indexes adds access counters. Include the database statistics reset timestamp so the result carries at least one boundary for the observation period.
SELECT
s.schemaname,
s.relname AS table_name,
s.indexrelname AS index_name,
s.idx_scan,
pg_relation_size(s.indexrelid) AS index_bytes,
d.stats_reset
FROM pg_stat_user_indexes AS s
CROSS JOIN (
SELECT stats_reset
FROM pg_stat_database
WHERE datname = current_database()
) AS d
ORDER BY index_bytes DESC
LIMIT 50;Check constraints before considering a change
This query identifies primary, unique, and exclusion properties and any constraint tied directly to the index. It is discovery evidence, not a complete dependency or rollout review.
SELECT
n.nspname AS schema_name,
t.relname AS table_name,
i.relname AS index_name,
x.indisprimary AS is_primary,
x.indisunique AS is_unique,
x.indisexclusion AS is_exclusion,
con.conname AS constraint_name,
con.contype AS constraint_type
FROM pg_index AS x
JOIN pg_class AS i ON i.oid = x.indexrelid
JOIN pg_class AS t ON t.oid = x.indrelid
JOIN pg_namespace AS n ON n.oid = t.relnamespace
LEFT JOIN pg_constraint AS con ON con.conindid = x.indexrelid
WHERE n.nspname NOT IN ('pg_catalog', 'information_schema')
AND n.nspname !~ '^pg_toast'
ORDER BY n.nspname, t.relname, i.relname
LIMIT 100;Interpret the evidence
| Evidence | What it supports | What it cannot prove |
|---|---|---|
| index_bytes | How much disk space the index relation currently uses. | Usefulness, bloat, or removal safety. |
| idx_scan | How often PostgreSQL initiated index searches in the available cumulative statistics. | The exact number of SQL queries or the value of the index. |
| stats_reset | A lower-bound timestamp for interpreting database-level cumulative counters. | That every related counter has an identical lifetime in every environment. |
| Constraint flags | Whether the index enforces primary, unique, or exclusion semantics. | Every application, query-plan, replica, or operational dependency. |
On current PostgreSQL, one executor scan can perform multiple index searches, so idx_scan should not be presented as an exact query count. Statistics also have visibility, update-lag, transaction snapshot, reset, and shutdown caveats.
Before changing an index
- Observe a representative workload period that includes rare, scheduled, and seasonal queries.
- Review constraints, dependencies, query plans, replicas, and application code paths.
- Estimate the write and storage tradeoff, then prepare a rollback and validation plan.
- Account for production locking. PostgreSQL documents
DROP INDEX CONCURRENTLYas a way to avoid blocking concurrent reads and writes, but it has restrictions and still requires deliberate operational review.
When DatabaseMeter can help
The PostgreSQL health check combines bounded table and index footprint with available cumulative scan counters and labels the result as an observation. Connected database telemetry can collect supported size and usage statistics over time, which gives a better review window than one pasted result.
DatabaseMeter does not treat a large index or a zero counter as automatic proof that an index should be dropped. Use the query analyzer to add bounded workload evidence without uploading SQL text.
Definitions and sources
Provider and PostgreSQL statements last reviewed August 29, 2026. Pricing consoles and invoices remain authoritative.
Related database resources
PostgreSQL health check
Analyze bounded PostgreSQL catalog statistics locally—without connecting your database.
Open resourcePostgreSQL database size guide
Use built-in PostgreSQL functions to inspect database and relation sizes.
Open resourcepg_stat_statements analyzer
Rank bounded query fingerprints from CSV, TSV, JSON, or an eligible connected database—without SQL text.
Open resource