なぜクエリプランナは「嘘」をつくのか? ― PostgreSQL統計情報の深淵を覗く
現場で長くPostgreSQLを触っていると、一度は必ず遭遇するはずです。「なぜ、明らかにデータが偏っているのに、プランナは全件スキャン(Seq Scan)を選択したんだ?」あるいは「インデックスがあるのに、なぜそれを使わない?」といった疑問です。
PostgreSQLのクエリプランナは優秀ですが、決して万能ではありません。彼らは常に「統計情報」という名の地図を頼りに目的地(実行計画)へ向かいます。もしその地図が古かったり、解像度が低かったりすれば、最適化の旅路は悲劇的な結果に終わります。
今日は、その地図の正体である統計情報システムカタログの「深層」について、少し掘り下げて話してみましょう。
—
統計情報の源泉:pg_statistic と pg_stats の違い
まず基本を押さえておきましょう。私たちが普段 `EXPLAIN` や `SELECT FROM pg_stats` で目にする `pg_stats` は、あくまで「人間が読みやすいように整形されたビュー」に過ぎません。
真の主役は、システムカタログである `pg_statistic` です。
- pg_statistic: プランナが直接参照するテーブル。ビットマスクや配列データが格納されており、人間には非常に読みづらい。
- pg_stats: `pg_statistic` をJOINして人間が理解できる形式に変換したビュー。
トラブルシューティングで最も重要なのは、`pg_stats` を見るだけでなく、データが極端に偏っている場合や、相関関係がある場合に「プランナがどう解釈しているか」を読み解く力です。特に `correlation`(物理的な行の順序とインデックスの順序の相関)の値は、インデックススキャンのコストを見積もる際の隠れた重要指標です。
プランナを惑わせる「サンプリング」の罠
`ANALYZE` を実行すると、PostgreSQLはテーブル全体を読みに行くわけではありません。設定された `default_statistics_target` に基づき、指定された割合の行をランダムにサンプリングします。
ここで注意すべきは、「統計情報の解像度」と「パフォーマンス」のトレードオフです。
デフォルトの100という値は、多くのケースで十分ですが、データ分布が極端なロングテール(べき乗則に近い分布など)を描いている場合、統計情報が実態を捉えきれないことがあります。特定のカラムに対してのみ、`ALTER TABLE … SET STATISTICS 500;` のように解像度を上げるテクニックは、熟練エンジニアの武器の一つです。
トラブルシューティング:統計情報が役に立たないとき
クエリプランナの誤算を解消する際、よくある誤解は「統計情報の鮮度が全て」と思い込むことです。実は、統計情報が新鮮でもプランナが誤った選択をするケースがあります。
1. 複数カラムの相関関係
PostgreSQLの通常の統計情報は「カラム単位」です。`WHERE col_a = 1 AND col_b = 2` といった条件があるとき、プランナは `col_a` のカーディナリティと `col_b` のカーディナリティを独立していると仮定して掛け合わせます。
もし `col_a` と `col_b` に強い相関がある場合、プランナのコスト見積もりは劇的に狂います。この場合、`CREATE STATISTICS` を使って「多変量統計情報」を作成し、プランナに相関関係を教える必要があります。これは非常に強力ですが、多くの現場で見落とされがちです。
2. 物理的な並び順と correlation
インデックススキャンが Seq Scan より遅いとプランナが判断する場合、`pg_stats` の `correlation` が極端に低いケースが多いです。物理的なデータ行がインデックス順序とバラバラであれば、ランダムアクセスのコストが跳ね上がるからです。これに気づかず、インデックスの性能を疑い続けるのは、エンジニアとして最も避けたい回り道です。
統計情報とどう向き合うべきか
結局のところ、統計情報は「近似値」です。DBエンジニアとしての私の経験則では、以下の3ステップを徹底するだけで、トラブルの8割は解決します。
1. 統計情報の「鮮度」を疑う: `pg_stat_user_tables` を見て、`last_analyze` がいつかを確認する。
2. 「歪み」を疑う: `pg_stats` の `most_common_vals` (MCV) と `most_common_freqs` を見比べ、実際のデータ分布と乖離がないか確認する。
3. 「関係性」を疑う: 複数カラムのフィルタリングが遅いなら、`CREATE STATISTICS` を検討する。
PostgreSQLは、ブラックボックスではありません。システムカタログを覗き込み、プランナと同じ視点でデータを見る。そうすることで、今まで「謎の低速クエリ」だったものが、明確な「根拠のある改善対象」に変わるはずです。
チューニングとは、プランナという優秀だが少し頑固な「相棒」と対話し、彼に正しい地図を渡してあげる作業です。ぜひ、次回のクエリ分析では `pg_stats` の奥深くに目を向けてみてください。そこには、クエリの成功を左右する全ての鍵が隠されています。
コメント