拡張統計情報の深淵:pg_stats_ext で「クエリプランナの盲点」を突く
PostgreSQLのクエリプランナは、優秀ではあるけれど、時として驚くほど「無知」になることがある。特に、複数のカラム間に相関関係がある場合だ。
「なぜ、この複雑な JOIN でコスト見積もりがこれほどまでに外れるのか?」
現場でそんな壁にぶつかったとき、多くのエンジニアが `EXPLAIN ANALYZE` を叩き、実行計画の「見積もり行数」と「実際の行数」の乖離に頭を抱える。その乖離の正体こそ、多くの場合「カラム間の独立性の仮定」というプランナの甘い期待にある。
今日は、PostgreSQLが抱えるその「盲点」を打破するための強力な武器、`pg_stats_ext` について、少し深掘りして話そうと思う。
—
なぜ「拡張統計」が必要なのか
PostgreSQLの標準的な統計情報(`pg_stats`)は、基本的には単一カラムに対する分布を保持している。もし `WHERE city = ‘Tokyo’ AND zip_code = ‘100-0001’` という条件があったとき、プランナは「cityの選択率」と「zip_codeの選択率」を独立したものとして計算し、それらを掛け算する。
しかし、実際には `Tokyo` と `100-0001` には強い相関がある。独立性を前提にすると、見積もりは現実よりも遥かに小さな値になり、結果としてプランナは Nested Loop を選択して爆死する。
ここで登場するのが `CREATE STATISTICS` で作成する拡張統計情報であり、その中身を覗くためのレンズが `pg_stats_ext` なんだ。
—
pg_stats_ext が教えてくれる「真実」
`pg_stats_ext` を眺めると、プランナがどれだけ必死に「現実」を把握しようとしているかがわかる。ここには主に以下の情報が詰まっている。
- ndistinct (多変量識別値数): 複数のカラムの組み合わせに対するユニークな値の数。
- dependencies (関数的依存性): 「Aが決まればBも決まる」という関係性。これがわかると、プランナは複雑なクエリでも極めて正確なコスト計算ができるようになる。
- mcv (最頻値リスト): 頻出する値の組み合わせと、その正確な出現頻度。
例えば、特定のカラムの組み合わせに対して `dependencies` が定義されていると、`pg_stats_ext` を確認することで、「このカラムとあのカラムは、データ的にこれだけ密接にリンクしている」という事実を定量的に把握できる。
トラブルシューティングの勘所
僕が現場でよく行うのは、`pg_stats_ext` と `pg_stats_ext_exprs` を突き合わせて、統計情報が十分に収集されているかを確認する作業だ。
もしパフォーマンスが芳しくないクエリがあれば、まず実行計画を見て、どのテーブルのどの結合条件で見積もりが大外ししているかを見つける。次に、以下のクエリを投げるのが定石だ。
SELECT stxname, stxkind, stxkeys
FROM pg_stats_ext
WHERE stxrelid = ‘target_table’::regclass;
ここで重要なのは、`stxkind` の値だ。`d` (dependencies) や `m` (mcv) が適切に設定されているかを確認し、もし統計情報が不足しているなら、`ANALYZE` を叩く前に `CREATE STATISTICS` を見直す勇気を持つこと。
ただ、一つ注意点がある。統計情報は「タダ」ではない。
あまりに多くの拡張統計を定義しすぎると、`ANALYZE` の時間が肥大化し、システム全体のオーバーヘッドになる。必要十分な統計を、ピンポイントで作成する。この「引き算の美学」こそが、熟練のDBAと初心者の分かれ道だと思う。
—
最後に:プランナと対話せよ
拡張統計情報は、単なるメタデータの集まりじゃない。あれは、我々エンジニアが「このデータの相関関係はこうなっているんだ」と、PostgreSQLのプランナに教え込むための「対話ツール」だ。
機械的なチューニングに疲れたら、一度 `pg_stats_ext` を眺めてみてほしい。クエリプランナという名の「有能だけど時に頑固なパートナー」が、何を根拠にその非効率なプランを選んだのか、その思考回路が透けて見えるはずだ。
データベースの最適化は、パズルではない。データの特性と、それを解釈するエンジンのアルゴリズムを理解し、橋渡しをする仕事だ。この記事が、君の次なる最適化の一助になれば幸いだ。
さて、そろそろ次のクエリの「実行計画」が君を呼んでいるんじゃないかな?
コメント