統計情報の「死角」を埋める:PostgreSQLにおける相関統計情報の深淵
PostgreSQLのクエリプランナを信頼しているエンジニアほど、一度はあの「見積もりの乖離」に絶望したことがあるはずです。
`EXPLAIN ANALYZE`を叩いて、実際の行数が100万件なのに見積もりが10件だった時。あるいは、その逆でネステッドループが爆発してシステムが悲鳴を上げた時。私たちは反射的にインデックスを疑い、あるいは`VACUUM ANALYZE`を走らせます。しかし、それでも解決しない場合、多くは「統計情報の限界」という壁にぶつかっています。
今日は、その壁を突き破るための武器、相関統計情報(Extended Statistics)について、少し深く掘り下げてみたいと思います。
—
なぜ、プランナは「嘘」をつくのか
PostgreSQLのデフォルトの統計情報収集(`ANALYZE`)は、基本的には列ごとのヒストグラムと最頻値(MCV)を管理するものです。
ここで問題になるのが、「列間の独立性」という甘い仮定です。
例えば、あるECサイトのデータベースで「都道府県」カラムと「郵便番号」カラムがあるとしましょう。プランナは「都道府県が東京都である確率」と「郵便番号が100-0001である確率」を個別に計算し、それらを掛け算して結合選択率を導き出します。
しかし、実際にはこの2つは強烈に相関しています。プランナは「東京都かつ100-0001」という条件を見たとき、実際よりもはるかに低い行数を見積もってしまう。これが、複雑なフィルタ条件でプランナが迷走する典型的な「統計情報の死角」です。
相関統計情報のアーキテクチャ
PostgreSQL 10で導入された相関統計情報は、この「掛け算の呪い」を解くための仕組みです。`CREATE STATISTICS`を実行すると、PostgreSQLは以下の情報をメタデータとして格納します。
- 機能的依存性 (Functional Dependencies): 片方の値が決まれば、もう片方が自ずと決まる関係(例:都市名と郵便番号)。
- 多変量最頻値 (Multivariate MCVs): 複数の列の組み合わせで頻出する値のパターン。
- n-distinct 係数: 複数の列を組み合わせた時のユニークな値の数。
これらは`pg_statistic_ext`カタログに格納され、プランナがコスト計算を行う際、単純な乗法公式に頼らず、これらの「実測値」を参照するようになります。
パフォーマンストラブルシューティングの勘所
もしあなたが「なぜかこのクエリだけプランナが Nested Loop を選んで死ぬんだ」と悩んでいるなら、まずは以下の手順で切り分けてみてください。
1. 見積もりの乖離を確認: `EXPLAIN ANALYZE` で `rows`(見積もり)と `actual rows`(実測)を比較します。桁が大きく違うなら、それは統計情報の問題です。
2. 相関の有無を疑う: フィルタ条件に使っている列同士に、論理的な関連性がないか考えます。「AならばBである」という依存関係があれば、間違いなく相関統計情報の出番です。
3. 統計を作成する:
CREATE STATISTICS stats_pref_zip
ON pref_code, zip_code FROM users;
これだけで、プランナが「あ、この2つはセットで動くものなんだ」と理解し始めます。
ただし、ここで一つ注意点があります。
相関統計情報も魔法ではありません。作成しすぎると、`ANALYZE`の負荷が高まり、カタログの更新コストが増大します。特に更新頻度が高いテーブルでは、過剰な統計作成はDB全体のパフォーマンス低下を招く「諸刃の剣」になり得ます。
最後に:エンジニアの直感と統計の融合
優れたデータベースエンジニアは、プランナが何を考えているのかをSQL越しに会話できる人です。
統計情報は、単なる設定値ではなく、データの「性質」そのものです。業務ロジックが変わればデータの相関性も変わり、プランナはまた別の嘘をつき始めます。技術の裏側にある「データの相関」を常に意識し、クエリとデータの関係を俯瞰する。
この地道な観察こそが、大規模システムを安定稼働させるための最大の武器になるはずです。
皆さんの現場でも、もし「謎の見積もり乖離」に遭遇したら、ぜひ `pg_statistic_ext` に思いを馳せてみてください。きっと、プランナが頭を抱えていた理由が見えてくるはずですよ。
コメント