

PostgreSQLのテーブルサイズ、概算で見るか正確に見るかで使う関数が違う
この記事は、構成と文章の整理にAIを使っています。
あくまで読みやすくするための補助で、内容そのものをAIにすべて任せて書いているわけではありませんが、ところどころ相談しながら整えています。
PostgreSQLでテーブルサイズを見る方法は大きく2系統ある。pg_classのrelpages/reltuplesから計算する方法と、pg_relation_size()系の関数を使う方法。どちらも「テーブルサイズ」と呼ばれるけど、性質が違うので使い分けが要る。
概算で見る: pg_classから計算する
pg_classのrelpages(ページ数)とreltuples(行数)を使うと、テーブルを直接読みに行かずに概算サイズが出せる。
SELECT relname as table_name, reltuples as row_num, ((relpages::int8 * 8192) / (1024 * 1024 * 1024)) as GBFROM pg_classWHERE relname = '<tablename>';relpagesはページ単位(デフォルト8192バイト)でのサイズなので、それに8192を掛けてバイト数にし、1024*1024*1024で割ってGB換算している。速い(pg_classを見るだけなのでロックを取りにいかない)けど、注意点が3つある。
整数の割り算で桁落ちする
relpages::int8 * 8192 はbigint、1024 * 1024 * 1024 もinteger。両方とも整数型なので、この割り算は整数同士の除算になる。つまり小数点以下は切り捨て。
1GB未満のテーブルだと結果が全部 0 になる。数百MBのテーブルを見たいときにこのクエリだと使い物にならない。
GB単位で見たいなら numeric にキャストするか、後述の pg_size_pretty() を使うほうが安全。
((relpages::numeric * 8192) / (1024 * 1024 * 1024)) as GBそもそも「概算値」でしかない
PostgreSQL公式ドキュメントにははっきり書いてある。効率のためreltuplesとrelpagesはリアルタイム更新されず、VACUUM・ANALYZE・一部のDDL(CREATE INDEXなど)のタイミングでしか更新されない。直近でVACUUM/ANALYZEが走ってないテーブルだと、実際のサイズと結構ズレることがある。
これはプランナー(クエリ実行計画を立てる側)が使うための統計値であって、「今この瞬間の正確なサイズ」を保証するものじゃない。用途がそもそも違う。
テーブル本体(heap)しか見てない
relpagesはテーブル本体のページ数だけをカウントしていて、インデックスやTOASTテーブルのサイズは含まれない。インデックスを何本も張ってるテーブルだと、実際にディスクを食ってる量よりかなり小さく出る。
「このテーブル、ディスクどれだけ使ってる?」を知りたい場合はこれだと過小評価になる。
正確に見る: pg_relation_size()系を使う
概算でよければrelpages/reltuplesのままで十分だし速い。正確な現在サイズが必要ならpg_relation_size()とpg_total_relation_size()を使うのが素直。
SELECT relname AS table_name, pg_size_pretty(pg_relation_size(oid)) AS table_only, pg_size_pretty(pg_total_relation_size(oid)) AS total_with_index_toastFROM pg_classWHERE relname = '<tablename>';pg_relation_size()はテーブルにロックを取りに行くので、他のトランザクションがAccessExclusiveLockを持ってると待たされることがある。ただし値自体は正確。
使い分け
- 大まかな傾向だけ見たい・速度優先 →
relpages/reltuplesのまま - 今のディスク使用量を正確に知りたい →
pg_relation_size()/pg_total_relation_size()+pg_size_pretty()
同じ「テーブルサイズ」でも、プランナー用の統計値と実際のディスクサイズは別物として扱うべきだった。