プランナの「思い込み」を正す:pg_stats_ext で統計情報の解像度を上げる
PostgreSQLを長く触っていると、一度は必ずぶち当たる壁がある。それは、「なぜ、この実行計画はこんなにも見積もりが外れているのか」という悩みだ。
EXPLAIN ANALYZEを叩いて、実際の行数(actual rows)と見積もり行数(estimated rows)が何桁もズレているのを見たとき、多くのエンジニアは「ANALYZEをすれば直るはずだ」と考えがちだ。しかし、それが単一列の偏りではなく、「列間の相関」に起因するものだとしたら、標準の統計情報だけではどうしようもない。
今日は、そんな行き詰まったクエリを救う切り札、`pg_stats_ext` ビューについて、少し深いところまで掘り下げて話そうと思う。
なぜ「相関」がプランナを狂わせるのか
PostgreSQLのプランナは、基本的には各列が独立しているという前提(独立性の仮定)でカーディナリティを見積もる。
例えば、`city`(都市)と `zip_code`(郵便番号)という2つのカラムを持つテーブルがあるとしよう。`WHERE city = ‘Tokyo’ AND zip_code = ‘100-0001’` というクエリを投げたとき、プランナは「Tokyoの出現率」と「その郵便番号の出現率」を個別に計算し、それらを掛け算して最終的な選択率(selectivity)を算出する。
だが現実には、東京という都市に大阪の郵便番号が存在するはずがない。データ間には強い相関がある。この前提の崩壊が、過小評価によるNested Loopの多用や、最悪なケースではインデックススキャンがフルスキャンに負けるといった悲劇を生むわけだ。
pg_stats_ext が教えてくれる「隠れた真実」
ここで登場するのが `pg_stats_ext` だ。これは単なるメタデータビューではない。CREATE STATISTICSコマンドで作成した「拡張統計情報」の裏側を覗き見するための、プランナの脳内を可視化する窓口だ。
SELECT FROM pg_stats_ext;
このビューを叩けば、どのテーブルのどのカラムに対して、どんな種類の統計が取られているかが一目瞭然になる。特に注目すべきは以下の3点だ。
- stxkind: ここには統計の種類がコードで入っている。『d』なら依存関係(dependencies)、『f』なら関数依存(functional dependencies)、『m』ならMCV(Most Common Values)リストだ。
- stxkeys: どの列の組み合わせがターゲットになっているか。
- stxexprs: 式ベースの統計情報が含まれているか。
特にマルチカラムの統計を取っている場合、`pg_stats_ext_exprs` や `pg_stats_ext_mcv` と併せて見ることで、「プランナがどの程度の解像度でデータの分布を把握しているか」を追跡できる。
トラブルシューティングの現場での立ち回り
もし君が「プランナがクエリのコストを激しく誤認している」現場に遭遇したら、まずは `pg_stats_ext` を確認してほしい。
1. 相関の特定: 頻繁にJOINやWHERE句で組み合わされる列があるなら、それらが `pg_stats_ext` に登録されているか確認する。もし登録されていなければ、迷わず `CREATE STATISTICS … (dependencies)` を実行する。
2. MCVリストの有効活用: データの分布が極端に偏っている(特定のキーだけ異常に多い)場合は、`dependencies` よりも `MCV` リストが強力だ。`CREATE STATISTICS … (mcv)` を検討してほしい。これにより、プランナは「よくあるパターン」については正確な行数を計算できるようになる。
3. オーバーヘッドとのトレードオフ: もちろん、何でもかんでも拡張統計を作ればいいわけではない。ANALYZEのコストは増大するし、カタログの肥大化も招く。私はいつも、「100万行を超えるテーブルで、かつ実行計画の誤認がビジネスロジックに悪影響を与えている箇所」に絞って適用するようにしている。
最後に:プランナは君の敵ではない
若いエンジニアから「PostgreSQLのオプティマイザは頭が悪い」という愚痴を聞くことがある。だが、それは少し違う。プランナは、我々が与えた不完全な統計情報に基づいて、極めて論理的に推論しているだけだ。
我々エンジニアの仕事は、エンジンを叩くことではなく、エンジンが正しい判断を下せるように「情報の解像度」をコントロールすることにある。`pg_stats_ext` は、そのための強力なレンズだ。
次にクエリが重いと感じたら、`EXPLAIN` を眺めるだけでなく、`pg_stats_ext` を開いてみてほしい。そこには、君のデータベースが語りたがっている「データの素顔」が隠されているはずだ。
さて、そろそろ次のクエリチューニングに戻らなければならない。皆さんのクエリが、今日も効率的なプランで実行されることを願っている。
コメント