「なぜか、急にクエリが遅くなった」。
運用現場でこれに直面したとき、多くのエンジニアがまずインデックスを疑います。もちろんそれは正しいアプローチの一つですが、インデックスを貼る前に「PostgreSQLがそのクエリをどう解釈しているか」、つまりプランナの思考を覗いてみる必要があるんです。
今日は、PostgreSQLの「脳内」を可視化する統計情報について、少し深い話をしましょう。
—
プランナを惑わせる「古い情報の罠」
PostgreSQLのプランナは、超優秀な参謀です。しかし、彼らが下す決断は、すべて「統計情報」という名の報告書に基づいています。もし、この報告書が古かったり、実態と乖離していたらどうなるか?
参謀は間違った戦術(非効率な実行計画)を選び、本番環境のCPUとメモリをドブに捨てることになります。
まずは、僕たちが普段お世話になっている主要なビューを整理しておきましょう。
1. `pg_stats`(まずはここから)
一番よく使うのがこれ。`pg_statistic`というシステムカタログは人間が読むには難解ですが、`pg_stats`はその要約版です。特定のカラムにどんな値がどれくらいあるのか、NULLはどれくらいか、という「データの傾向」が一目瞭然です。
2. `pg_class`(テーブルの健康診断)
テーブルごとの行数(`reltuples`)やページ数(`relpages`)が格納されています。「最近大量にデータ消したのに、なぜかクエリが遅いまま……」という時は、ここを見ると原因がすぐわかります。
—
現場で役立つ「統計情報の覗き方」
例えば、特定のカラムに対するクエリが異常に遅いとき。僕はまず、そのカラムの統計情報を確認します。
SELECT
attname AS column_name,
n_distinct, — ユニークな値の数
most_common_vals, — よく出現する値
most_common_freqs — それらの出現頻度
FROM pg_stats
WHERE tablename = ‘orders’
AND attname = ‘status’;
ここで注目すべきは `n_distinct` です。
もし実際のデータのユニーク数が100万件あるのに、ここが `-1` (全件ユニークと推定)ではなく、極端に小さい数字になっていたら? プランナは「このカラムで絞り込めば一瞬で終わるはずだ!」と楽観視し、インデックスを使わずにフルスキャンを選択したりします。
—
「なぜか遅い」を解決する実践的な手順
もしクエリの実行計画(`EXPLAIN ANALYZE`)を見て、「推定行数」と「実際の行数」に大きな乖離があるなら、それは統計情報が死んでいるサインです。
そんな時、僕が現場でやることはシンプルです。
1. analyzeを手動で叩く
一番の特効薬は `ANALYZE` です。
ANALYZE VERBOSE orders;
これだけで直るケースが7割。最近のPostgreSQLはオートバキュームが優秀ですが、大量のデータ更新・削除があった直後は、手動で追い打ちをかけるのがエンジニアの流儀です。
2. 統計の精度を上げる
もしデータの偏りが激しいカラム(例えば「処理待ち」が99%を占めるカラムなど)があるなら、デフォルトのサンプリングではプランナが騙されます。そんな時は、カラム単位で統計情報の収集精度を上げましょう。
ALTER TABLE orders ALTER COLUMN status SET STATISTICS 1000;
ANALYZE orders;
デフォルトは100ですが、これを最大1000まで増やすことで、プランナはより詳細なヒストグラムを作成し、賢い判断を下せるようになります。
—
最後に:統計情報は「生き物」だと思え
統計情報は、データベースの体温計のようなものです。
「インデックスを貼ったのに効かない」「Nested Loopが選ばれてほしいのにHash Joinになる」といった悩みは、たいてい統計情報のズレが原因です。
「魔法のクエリ」を探す前に、まずはPostgreSQLが現状をどう認識しているかを確認する。 これができるようになると、チューニングの打率が格段に上がります。
もし皆さんの現場で、「なぜか遅い」クエリが居座っていたら、ぜひ `pg_stats` を覗いてみてください。PostgreSQLが、本当はもっと効率的な道があることを教えてくれるはずですよ。
それでは、良いチューニングライフを!
コメント