「あれ、なんか急にクエリが遅くなったぞ?」
そんな経験、一度や二度じゃないはずです。昨日まで爆速だったJOINが、なぜか今日はフルスキャン(全件走査)を始めて、CPUを食いつぶしている。そんな時、一番最初に疑うべきは「統計情報の鮮度」です。
今日はPostgreSQLの隠れた(でも一番重要な)立役者、`ANALYZE`コマンドについて話をしよう。教科書的な説明はドキュメントに任せるとして、現場で「何が起きているのか」「どう付き合うべきか」という話を深掘りしていくよ。
—
なぜ、PostgreSQLは「勘違い」をするのか?
まず、大前提を共有しておこう。PostgreSQLのクエリプランナは、超優秀な参謀だけど、「今のテーブルがどんな状態か」を自分の目では見ていない。
プランナが見ているのは、`pg_statistic`というテーブルに記録された「統計情報」という名のメモ帳だけなんだ。
「このカラムには値が何種類あるか」「NULLはどれくらいあるか」「値の分布はどうなっているか」。このメモ帳が古ければ、プランナは平気で「全件走査したほうが速いよ」なんて的外れなアドバイスをしてくる。
だからこそ、定期的な「情報の更新(=`ANALYZE`)」が必要になるわけだ。
実践:ANALYZEの使い分け
`ANALYZE`には大きく分けて2つの付き合い方がある。
1. 自動実行(Autovacuum)に任せる場合
基本的には、PostgreSQLのバックグラウンドプロセスである`autovacuum`がやってくれる。こいつは「お、このテーブル結構データ更新されたな」と判断したら、自動で`ANALYZE`をかけてくれるんだ。
だから、普通のアプリケーションなら基本的には放置でいい。でも、ここで落とし穴がある。
2. 手動で叩くべき「危ないタイミング」
以下のようなケースでは、`autovacuum`を待たずに、手動で叩くのが現場の鉄則だ。
- バルクインサートの後: 数百万件のデータを一気に流し込んだ直後。統計情報が古いままだと、プランナは「データはまだ少ない」と勘違いして、インデックスを使わない選択をしがちだ。
- 大規模なUPDATE/DELETEの後: データの分布がガラッと変わるような操作をした直後。
- 特定のクエリだけ急激に遅くなった時: 「おかしいな?」と思ったら、迷わず打つ。これが現場の正解だ。
— テーブル全体の統計情報を更新
ANALYZE users;
— 特定のテーブルの、特定のカラムだけを更新(時間がかかるときはこれ)
ANALYZE VERBOSE users (email, created_at);
※ `VERBOSE`をつけると、どのくらいの行をサンプリングしたか等のログが出るから、現場のデバッグには重宝するぞ。
「インデックスはあるのに使われない」の正体
よく後輩から「インデックスを貼ったのにクエリプランが変わらないんです!」と泣きつかれることがある。そんな時、`EXPLAIN`の結果を見ると、案の定「見積もり行数」と「実際の行数」に桁違いの乖離があるんだ。
もし、`ANALYZE`をかけても解決しないなら、それは統計情報の「解像度」の問題かもしれない。
— 特定のカラムの統計情報の解像度を上げる(デフォルトは低め)
ALTER TABLE users ALTER COLUMN status SET STATISTICS 1000;
ANALYZE users;
デフォルトの統計情報保持数(`default_statistics_target`)は通常100なんだけど、値の種類が極端に多いカラム(例えばUUIDや複雑なID)の場合は、これを上げてやることでプランナが「あ、ここには特定の値が偏ってるんだな」と気づいてくれるようになる。
最後に:先輩からのアドバイス
`ANALYZE`は、いわば「視力の矯正」だ。眼鏡をかけないと、プランナはぼやけた視界の中で「なんとなくこっちが速そう」と適当にルートを決めることになる。
- 「遅いな」と思ったら、まずは`ANALYZE`を疑う。
- 大量のデータ投入後は、習慣的に`ANALYZE`する。
- それでもダメなら`STATISTICS`の値を調整する。
この3つを頭に入れておくだけで、現場でのトラブルシュートのスピードは劇的に変わるはずだ。
データベースをチューニングするのは、エンジンを調整するようなもの。理屈を知れば、PostgreSQLは最高のパフォーマンスで応えてくれる。ぜひ、明日からの運用で意識してみてくれ!
コメント