「なぜか急にクエリが遅くなった」。
データベースエンジニアとして長く現場にいると、この相談を何度受けるかわかりません。インデックスも貼ってある、テーブル定義も間違っていない。それなのに、PostgreSQLが謎のフルスキャンを選択して爆死する――。
この原因の8割は、統計情報の「鮮度」にあります。今日は、PostgreSQLの頭脳であるクエリプランナーを正しく導くための「統計情報とANALYZE」について、現場の勘所を交えて話そうと思います。
—
クエリプランナーは「勘」で動いているわけじゃない
PostgreSQLが実行計画(EXPLAINの結果)を立てるとき、何を基準に「インデックスを使うか、それとも全スキャンか」を決めているか知っていますか?
実は、プランナーはテーブルの全データを毎回読み込んでいるわけではありません。そんなことをしたらクエリが終わる前に日が暮れてしまいます。彼らが頼りにしているのは、`pg_statistic` というカタログビューに記録された「統計情報のメモ」です。
- テーブルの全行数はどれくらいか?
- このカラムにはどんな値がどれくらいの頻度で現れるか?(ヒストグラムや最頻値)
- NULLはどれくらい混ざっているか?
プランナーは、この情報を基に「この条件なら、だいたいこれくらいの行数がヒットするはずだ。なら、このインデックスを使ったほうが速いな」とコスト計算をしているんです。
「統計情報が古い」という致命傷
ここからが本題です。この「メモ」である統計情報は、放っておくとどんどん現実と乖離していきます。
例えば、数百万行あるテーブルの統計情報が、数ヶ月前の「空っぽだった頃」のままだったらどうなるでしょう? プランナーは「このテーブルは小さいから、インデックスを引くより全スキャンしたほうが速いな」という誤った判断を下します。これが、パフォーマンス劣化の典型的なパターンです。
ANALYZEの自動化、頼りすぎは禁物
PostgreSQLには `autovacuum` という優秀な掃除係がいて、デフォルトではこいつが統計情報の更新(ANALYZE)も兼務してくれています。
`postgresql.conf` の `autovacuum_analyze_scale_factor` を見ればわかりますが、標準では「テーブルの行数が20%くらい変わったら更新する」という設定になっているはずです。
しかし、大規模なデータ更新が行われるバッチ処理の後などは、この自動更新を待っていられないことがあります。ここで、我々エンジニアの出番です。
実践:いつ、どうやってANALYZEを打つか
「なんとなく遅いからANALYZEしておこう」というのは初心者です。まずは現状の統計情報がいつ更新されたかを確認しましょう。
SELECT relname, last_analyze, last_autoanalyze
FROM pg_stat_user_tables
WHERE relname = ‘your_table_name’;
もし、大量のデータを投入・削除した直後で、ここの日時が古ければ、迷わず手動で叩きます。
— テーブル全体をガッツリ解析
ANALYZE VERBOSE your_table_name;
注意点:統計情報の「精度」をいじる
特定のカラムに対して複雑なクエリを投げている場合、標準の解析精度では足りないことがあります。そんなときは、カラム単位で統計情報の精度を上げてやりましょう。
— 特定のカラムだけ統計情報の精度を上げる(デフォルトは0=基本値)
ALTER TABLE your_table_name ALTER COLUMN target_column SET STATISTICS 500;
ANALYZE your_table_name;
これをやると、ヒストグラムのバケット数が増えて、クエリプランナーがより精密な「行数見積もり」ができるようになります。ただし、やりすぎるとANALYZE自体が重くなるので注意してください。
現場からのアドバイス:運用で気をつけるべきこと
最後に、後輩のみんなに伝えておきたい「現場の知恵」を二つだけ。
1. バッチ処理の後には必ずANALYZEを
もし大規模なトランザクションを一括で流し込むバッチ処理があるなら、そのスクリプトの最後に `ANALYZE` を仕込むのが鉄則です。cronで定期実行するのではなく、データの更新タイミングに合わせて実行するのが最も賢いやり方です。
2. 「遅い」の正体を突き止める
クエリが遅いとき、`EXPLAIN ANALYZE` を叩いてください。そこで `actual rows`(実際の結果行数)と `estimated rows`(プランナーが見積もった行数)が大きく乖離していたら、統計情報の鮮度不足が確定です。
—
PostgreSQLは、放っておいてもそれなりに動いてくれる「いい子」ですが、たまにこうやって「現実を見せてやる」必要があります。
データベースは生き物です。中身が変われば、最適な道順も変わる。その変化をプランナーに正しく伝えてあげることこそが、インデックスを闇雲に増やすよりも遥かに効果的なチューニングになるはずですよ。
それでは、また現場で会いましょう!
コメント