【実務・中級編】 MCVリスト統計 – PostgreSQL

「なぜかこのクエリだけ遅い」を解決する。MCVリストと統計情報の深淵

現場でバリバリコードを書いていると、一度は経験するはずです。「開発環境では爆速だったのに、本番環境の数百万件のデータになった途端、クエリが息切れする」という悪夢。

実行計画を `EXPLAIN ANALYZE` で覗いてみると、PostgreSQLのオプティマイザが「返ってくる行数」を盛大に見誤っていて、Nested Loopを強行して自爆している……そんな場面、よくありますよね。

今日は、そんな「見積もりのズレ」を修正する切り札、MCVリスト(Most Common Values)の話をしよう。これを知っているだけで、現場でのトラブルシューティングの引き出しが一段深くなるはずだよ。

—

そもそも、オプティマイザは何を見てる?

PostgreSQLのオプティマイザは魔法使いじゃない。統計情報という「地図」を頼りに、最短ルートを探しているんだ。

デフォルトでは、カラム内のデータの分布は「なんとなく均一に近い」と仮定されることが多い。でも、現実はそんなに甘くないよね。例えば、「ステータス」カラムで「完了」が9割を占めていて、「エラー」はごくわずか、みたいな偏りがあるデータ。

このとき、PostgreSQLはどうやって「検索結果が何行になるか」を予想しているのか。そこで登場するのが MCVリスト だ。

MCVリストとは何か?

MCVリストは、`pg_stats` ビューの中にひっそりと記録されている「そのカラムで特によく出現する値のリスト」のこと。

PostgreSQLは `ANALYZE` コマンドを実行した時に、テーブルをサンプリングして、「この値は頻繁に出るな」というトップランカーたちをリストアップしているんだ。

  • MCVがある場合: オプティマイザは「あ、これ頻出値だ。じゃあ統計データに基づいてこれくらいの行数だな」と正確に見積もれる。
  • MCVがない(あるいは範囲外)場合: 「たぶんこれくらいだろう…」という推測(ヒストグラムや推定値)で動く。これが、大規模テーブルで「Nested Loopの悲劇」を招く原因だ。

実践:MCVを確認してみよう

まずは、自分の環境でどうなっているか見てみよう。例えば、注文管理テーブルの `order_status` カラムが偏っているとする。

SELECT
column_name,
n_distinct, — 値の種類の数
most_common_vals, — MCVリスト
most_common_freqs — それぞれの出現頻度
FROM pg_stats
WHERE tablename = ‘orders’
AND column_name = ‘order_status’;

この `most_common_vals` に、頻出する値が配列で入っているはずだ。もしここが空だったり、古い情報のままだったりすると、実行計画はとたんにポンコツになる。

「あれ、おかしいな?」と思ったら試すこと

もし特定のクエリで「行数見積もりが明らかにズレている」と感じたら、まずは統計情報の鮮度を疑おう。

— テーブル全体の統計を強制更新
ANALYZE orders;

これだけで直ることも多い。でも、それでも直らない場合、統計情報の収集精度が足りていない可能性がある。

— このカラムの統計情報をより詳細に収集するように設定
ALTER TABLE orders ALTER COLUMN order_status SET STATISTICS 1000;
ANALYZE orders;

`SET STATISTICS` のデフォルト値は通常100なんだけど、これを大きくすると、MCVリストに登録される値の数が増え、ヒストグラムの分解能も上がる。ただし、その分 `ANALYZE` の負荷と統計情報のサイズは増えるから、闇雲に大きくせず、慎重に調整するのがコツだ。

現場の先輩からのアドバイス

最後に、これだけは覚えておいてほしい。

「統計情報がすべてではない」ということ。

MCVリストをチューニングしても改善しない場合、それはクエリの書き方そのものに問題があることが多い。例えば、カラムに対して関数を噛ませていたり(`WHERE UPPER(status) = ‘DONE’`)、相関サブクエリが多すぎたり。

「統計をいじって直すのは最後の手段。まずはクエリを素直に書くこと」。

これを意識した上で、どうしても性能が出ない時の「最後の隠し球」としてMCVリストを調整する。この順番を間違えないようにね。

—

技術の深掘りは楽しいものだ。今日解説した `pg_stats` の中身を覗いてみるだけでも、PostgreSQLの「見ている世界」が少しクリアに見えてくるはず。ぜひ、次のチューニング案件で試してみてくれ。

また何か詰まったら、いつでも聞きに来てよ。エンジニア同士、一緒に成長していこう。

コメント

タイトルとURLをコピーしました