「最近、クエリが遅い気がするんだけど、どこから手をつけていいか分からない……」
現場でそんな相談を受けることがよくあります。そんな時、僕がまず最初に叩くのが `pg_stat_user_tables` です。
ドキュメントを読めば載っている情報ですが、実務でどう「味付け」して読み解くかを知っているかどうかで、トラブルシューティングのスピードは劇的に変わります。今日は、PostgreSQLの運用において欠かせないこのビューと、僕が普段やっている「テーブルの健康診断」について話をしましょう。
—
なぜ `pg_stat_user_tables` が重要なのか?
一言で言えば、「データベースがテーブルとどう付き合っているか」を一番正直に教えてくれる場所だからです。
PostgreSQLは、テーブルに対してどんな読み取り(スキャン)が行われたのか、どれくらいデータが書き換わったのかを、バックグラウンドで黙々と記録しています。この統計情報は「何が起きているか」を推測するのではなく、「何が起きているか」を観測するための最高の武器なんです。
頻出する「見るべきカラム」たち
全部を暗記する必要はありません。僕が現場でパッと確認するのは以下の項目です。
- seq_scan: テーブルフルスキャンされた回数。これが異常に多いテーブルは、インデックスが足りていない可能性大です。
- idx_scan: インデックス経由でスキャンされた回数。
- n_tup_ins / n_tup_upd / n_tup_del: それぞれ挿入、更新、削除の回数。
- n_live_tup / n_dead_tup: 現在の「生きた」データ数と、「死んだ(更新・削除で不要になった)」データ数。
—
実践:僕の「お決まり」チェッククエリ
単にビューを見るだけだと数字の羅列で頭が痛くなりますよね。実務では、以下のようなクエリで「今、何が起きているか」を可視化しています。
特に最近のPostgreSQL運用で重視しているのは「デッドタプルの溜まり具合」です。
SELECT
relname AS table_name,
n_live_tup,
n_dead_tup,
— デッドタプルの割合を算出
round(n_dead_tup::numeric / nullif(n_live_tup + n_dead_tup, 0) 100, 2) AS dead_ratio,
last_autovacuum,
last_autoanalyze
FROM
pg_stat_user_tables
WHERE
(n_live_tup + n_dead_tup) > 1000 — 小さなテーブルは無視
ORDER BY
n_dead_tup DESC;
このクエリの狙い
「デッドタプル率(dead_ratio)」が高いテーブルは、Autovacuumが追いついていないか、更新頻度が激しすぎて整理が追いついていない証拠です。これ放置すると、クエリが古いゴミデータまで読み込んでしまい、パフォーマンスがガタ落ちします。
「あれ、最近あのテーブル重くない?」と思ったら、まずはこのクエリを流して「おっと、デッドタプルが30%を超えてるぞ」と特定する。これが僕のトラブルシューティングの第一歩です。
—
統計情報とどう向き合うか:先輩からのアドバイス
最後に、統計情報を扱う上での心構えを一つ。
「統計情報は嘘をつかないが、見落としはある」ということです。
例えば、`seq_scan` が多くても、そのテーブルが元々数行しかない小さなマスタデータなら全く問題ありません。逆に、数百万行あるテーブルで `seq_scan` が1回でも走っていたら、それは「いつか大きな障害になる種」かもしれません。
数字そのものを見るのではなく、「アプリの仕様上、このテーブルはどれくらいの頻度でアクセスされるべきか?」というビジネス側の文脈を重ね合わせて見てください。
- 「このマスタは参照しかしないはずなのに、なんで `n_tup_upd` が増えてるんだ?」
- 「特定の期間だけ `seq_scan` が跳ね上がるのは、あのバッチ処理の影響か?」
こういう「違和感」に気づけるようになると、データベースエンジニアとしての腕はグッと上がります。
—
もし皆さんの現場で「なんとなく重い」という謎の不調に悩まされているなら、まずは `pg_stat_user_tables` を覗いてみてください。PostgreSQLは、きっとあなたの質問に対して、統計情報という形で答えを返してくれるはずですよ。
それでは、また次回の記事でお会いしましょう。良いチューニングライフを!
コメント