オプティマイザの「目」を疑え:pg_statsで紐解く実行計画の真実
PostgreSQLで「なぜか遅いクエリ」に直面したとき、皆さんはまず何を確認しますか? `EXPLAIN ANALYZE` を叩いて、コストの見積もりと実際の実行時間に乖離があることを見つける。これはエンジニアにとって日常茶飯事です。
しかし、多くの人がそこで立ち止まってしまう。「推定コストは妥当なのに、なぜNested Loopが選ばれたのか?」「なぜこのインデックスが無視されたのか?」
その答えは、大抵の場合 `pg_stats` の中に眠っています。今日は、オプティマイザの「目」である統計情報について、少し深い話をしようと思います。
オプティマイザは「勘」で動いているわけではない
PostgreSQLのクエリプランナは、魔法使いではありません。彼らは `pg_stats` ビューに記録された「過去の事実」に基づいて、論理的な推測を繰り返しています。
もしプランナが間違った選択をしたなら、それはプランナが馬鹿なのではなく、`pg_stats` が提供する情報が実態と乖離している可能性が高い。まずは、このビューが単なる情報の羅列ではなく、統計学的な武器であることを認識する必要があります。
ヒストグラムとMCVの「解像度」を理解する
`pg_stats` で特に注目すべきは `most_common_vals` (MCV) と `histogram_bounds` です。
- MCV (頻出値): データ分布の「スパイク」です。特定のキーにデータが集中している場合、プランナはここを見て「この値は全体の何%を占めるか」を正確に計算します。
- ヒストグラム: それ以外の「平坦な分布」を表現します。
ここで重要なのは、「統計情報の解像度」です。デフォルトの `default_statistics_target` は100ですが、複雑なデータ分布(例えば、べき乗則に従うような偏りのあるカラム)では、この100という粒度では分布を捉えきれないことが多々あります。
あるカラムのフィルタリング条件で、推定行数と実行数が100倍ズレている場合、真っ先に疑うべきは統計ターゲットの不足です。`ALTER TABLE … ALTER COLUMN … SET STATISTICS 500` といったコマンドを打つだけで、プランナが急に賢い選択をし始める光景を、私は何度も目にしてきました。
「相関」という見えない壁
`pg_stats` を見ても問題が見当たらないのに、なぜかプランナが結合順序を誤ることがあります。その原因の多くは、カラム間の「相関」にあります。
PostgreSQLの基本的な統計情報は、あくまで「カラム単体」の分布です。`WHERE city = ‘Tokyo’ AND zip_code = ‘100-0001’` という条件があったとき、プランナはこれらを独立した確率として計算し、乗算してしまいます。しかし、実際にはZipコードと都市には強い相関がありますよね。
ここで登場するのが 「拡張統計情報(Extended Statistics)」 です。`CREATE STATISTICS` を使ってカラム間の相関をプランナに教えてあげること。これが、熟練エンジニアが最後の一手を打つための必須スキルです。これを使えば、`pg_stats` の限界を超えた高精度な見積もりが可能になります。
トラブルシューティングの思考回路
現場でパフォーマンスチューニングを行う際、私はいつもこんな順番で考えています。
1. 乖離の確認: `EXPLAIN ANALYZE` で推定行数と実際の行数の乖離を確認する。
2. 分布の確認: `pg_stats` を覗き、MCVやヒストグラムが実際のデータ分布を反映しているか(NULLの割合や、極端な偏りがないか)を確認する。
3. 統計の鮮度: `last_analyzed` を見る。自動バキュームが追いついていないなら、手動で `ANALYZE` をかけてみる。
4. 解像度の引き上げ: それでもダメなら `SET STATISTICS` で解像度を上げ、それでもダメなら `CREATE STATISTICS` で相関を定義する。
最後に:統計情報は「生き物」だ
`pg_stats` は静的なビューではありません。データが更新されれば、統計情報もまた変化し続けます。
多くのエンジニアが「一度チューニングして終わり」にしてしまいがちですが、データが変われば最適な実行計画も変わります。統計情報を正しく理解し、定期的に `pg_stats` を眺める習慣をつけることは、まるで自分の育てているシステムの「健康状態」を診断するようなもの。
PostgreSQLは、非常に正直なデータベースです。私たちが正しい情報を与えれば、必ず正しい回答を返してくれます。もしあなたのDBが機嫌を損ねているなら、まずは `pg_stats` を開いて、彼らが何を「見ている」のかを語り合ってみてください。
そこには、ドキュメントには載っていない、生きたデータの物語が刻まれているはずです。
コメント