Come ottenere le dimensioni di tabelle, indici e database in PostgreSQL
La query che tutti vogliono davvero — le tabelle più grandi, indici inclusi — poi le tre funzioni di dimensione e cosa misura realmente ciascuna.
-- Le 20 tabelle più grandi (indici e TOAST inclusi):
SELECT c.oid::regclass AS table_name,
pg_size_pretty(pg_total_relation_size(c.oid)) AS total,
pg_size_pretty(pg_table_size(c.oid)) AS table_only,
pg_size_pretty(pg_indexes_size(c.oid)) AS indexes
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind = 'r'
AND n.nspname NOT IN ('pg_catalog', 'information_schema')
ORDER BY pg_total_relation_size(c.oid) DESC
LIMIT 20;
Le tre funzioni di dimensione, chiarite
| Funzione | Cosa misura |
|---|---|
pg_relation_size('t') | Solo lo heap principale — nessun indice, nessun TOAST (lo storage out-of-line per i valori grandi). |
pg_table_size('t') | La tabella vera e propria: heap + TOAST + mappe ausiliarie. Ancora nessun indice. |
pg_total_relation_size('t') | Tutto: tabella + TOAST + tutti i suoi indici. È "quanto disco mi costa questa tabella". |
Racchiudi una qualsiasi di esse in pg_size_pretty(...) per unità leggibili. Una tabella con colonne di testo/JSONB lunghe può avere la maggior parte dei suoi byte nel TOAST — se pg_table_size supera di gran lunga pg_relation_size, è lì che sono.
Altre one-liner
-- Dimensione di una singola tabella / indice / database:
SELECT pg_size_pretty(pg_total_relation_size('public.orders'));
SELECT pg_size_pretty(pg_relation_size('orders_created_at_idx'));
SELECT pg_size_pretty(pg_database_size(current_database()));
-- Tutti i database del cluster:
SELECT datname, pg_size_pretty(pg_database_size(datname))
FROM pg_database
ORDER BY pg_database_size(datname) DESC;
-- Gli indici più grandi:
SELECT c.oid::regclass AS index_name,
pg_size_pretty(pg_relation_size(c.oid)) AS size
FROM pg_class c
WHERE c.relkind = 'i'
ORDER BY pg_relation_size(c.oid) DESC
LIMIT 20;
Dimensione su disco ≠ dati vivi
Queste funzioni misurano il disco allocato, che include il bloat: lo spazio occupato dalle versioni morte delle righe che il vacuum non ha recuperato (e che il vacuum semplice restituisce alla tabella, non al SO). Una tabella da 50 GB potrebbe contenere 20 GB di righe vive. Se una tabella sembra troppo grande per il suo numero di righe, quella è un'indagine sul bloat, non un upgrade di storage.
Tabelle partizionate: il padre in sé è vuoto — somma le partizioni, ad esempio con una join su pg_inherits, oppure dai loro un'occhiata con \d+ parent_name in psql.
