統計情報という名の「羅針盤」:PostgreSQLオプティマイザの深淵を覗く
PostgreSQLのクエリチューニングにおいて、最も「裏切られる」瞬間といえば、やはり実行計画(EXPLAIN)のコスト見積もりが現実と乖離しているときでしょう。
「数百万行あるテーブルなのに、なぜオプティマイザはインデックススキャンを選んだのか?」
「ネステッドループの中に潜む、あの忌まわしい Seq Scan はどこから来たのか?」
長年PostgreSQLと付き合っているエンジニアなら一度は頭を抱えたことがあるはずです。結局のところ、オプティマイザは万能の予言者ではありません。彼らが未来を予測するために参照しているのは、`pg_statistic` という名の、極めて簡素で、しかし極めて重要な「統計情報の断片」だけなのです。
今日は、その統計情報がどう生成され、プランナがいかにしてそれを解釈し、我々のクエリを導いているのか、その「内側」の話をしましょう。
—
ANALYZEの裏側:情報の「サンプリング」という妥協
まず、`ANALYZE` コマンドが何をしているか。これは全件スキャンではありません。全件をスキャンしていたら、巨大なテーブルではそれだけでシステムが止まってしまいます。
PostgreSQLは、テーブルからランダムにサンプリングしてデータを読み込みます。このとき重要なパラメータが `default_statistics_target` です。デフォルトの「100」というのは、各カラムについて100個のバケット(ヒストグラム用)を作るという意味ですが、データの分布が偏っているカラムでは、この数字が仇になることもあります。
経験則として言えるのは、分布が複雑なカラムに対しては、この値を引き上げる(例えば300や500にする)ことが、インデックスの有効活用への近道だということです。 ただし、やりすぎは `ANALYZE` 自体のオーバーヘッドと統計情報の肥大化を招くので、あくまで「ここぞというカラム」に絞るのがプロの作法です。
—
pg_statistic:プランナが頼る「地図」の正体
`pg_statistic`(およびその可読版である `pg_stats` ビュー)の中身を見てみると、大きく分けて2つの重要な情報が並んでいます。
1. MCV (Most Common Values):最頻値リスト
そのカラムで最も頻繁に出現する値のセットです。オプティマイザは、「WHERE col = ‘A’」のようなクエリが来たとき、まずMCVを確認します。もし ‘A’ がリスト内にあれば、その頻度をそのまま適用してコストを算出します。シンプルですが、非常に強力です。
2. ヒストグラム (Histogram)
範囲検索(WHERE col > 100 AND col < 500)の際に使われます。データ全体を等しい確率(頻度)になるようにバケットに分割したものです。
ここで陥りやすい罠があります。
もし、MCVに含まれない値や、ヒストグラムの境界付近のデータに対してクエリを投げると、プランナは「補間」という名の推論を行います。この推論が外れたとき、悲劇的な実行計画が生成されるわけです。特に、相関関係のあるカラム(例:都道府県コードと郵便番号)を複数条件で絞り込む場合、プランナは「それぞれの確率は独立している」と仮定して計算してしまいます。結果、見積もり件数と実件数に桁違いのズレが生じるのです。
—
パフォーマンストラブルシューティング:統計情報は「鮮度」が命
「クエリが遅い」という相談を受けたとき、真っ先に確認すべきは統計情報の最終更新日時です。
SELECT relname, last_analyze, last_autoanalyze FROM pg_stat_user_tables;
もし、大量のデータ更新があったにも関わらず `last_autoanalyze` が古いままなら、自動統計情報収集(autovacuum)が追いついていないか、閾値の設定が甘い可能性があります。
特に、INSERTが中心のログテーブルなどでよく起きるのですが、テーブルの更新率が低いとみなされ、`autovacuum` が動かないことがあります。このようなケースでは、手動で `ANALYZE` を叩くか、`autovacuum_analyze_scale_factor` を調整して、より敏感に反応するように設定を追い込む必要があります。
—
最後に:オプティマイザと対話するということ
統計情報は、単なるデータのサマリーではありません。データベースエンジニアにとって、それは「オプティマイザとの対話手段」です。
クエリが遅いとき、プランナを責める前に「自分はオプティマイザに、このデータの偏りを正しく伝えられているか?」を自問してみてください。ヒストグラムの解像度は十分か? MCVに重要な値が含まれているか?
PostgreSQLの深淵は、こうした細かい調整の積み重ねの中にこそあります。魔法の杖など存在しません。あるのは、エンジニアの細やかな観察と、統計情報という名の羅針盤を正しく読み解く力だけです。
もし今、あなたのデータベースが不可解なプランを選択しているなら、一度 `pg_stats` を覗いてみてください。そこに、解決の糸口が必ず眠っているはずです。
コメント