【実務・中級編】 MCV(Most Common Values) – PostgreSQL

「なぜかクエリが遅い」を解決する第一歩:PostgreSQLの「MCV」と付き合う話

現場でバリバリコードを書いていると、たまに遭遇するじゃないですか。「インデックスも貼ってあるし、`EXPLAIN ANALYZE`を見てもコスト計算は合っていそうなのに、なぜか実行計画がめちゃくちゃなやつ」。

その原因、実はPostgreSQLが持っている「統計情報」の勘違いかもしれません。特に、MCV(Most Common Values)の理解は、データベースエンジニアとして一つ上のレベルに行くための必須スキルです。今日は、この隠れた立役者について、実務的な話をしようと思います。

—

MCVって、結局なんなの?

一言で言うと、「そのカラムの中で『超よく出てくる値』のVIPリスト」です。

PostgreSQLはテーブルをスキャンする際、いちいち全件検索なんてしていられませんよね。だから、`ANALYZE`コマンドで統計情報を取って、「だいたいこれくらいの件数だろう」と見積もりを立ててプランを決めます。

その際、カラム内の値の分布が偏っている場合、PostgreSQLは「あ、この値は全体の30%を占めてるな」という情報を保持します。これがMCVです。オプティマイザはこのリストを見て、「この値で検索するなら、インデックススキャンよりシーケンシャルスキャンの方が速そうだな」といった賢い判断を下すわけです。

なぜMCVが「地雷」になることがあるのか

理論上は完璧なはずのMCVですが、実務でトラブルになるのは「分布の変化」です。

例えば、あるECサイトの「注文ステータス」カラムを考えてみてください。
通常は「完了」や「キャンセル」が大部分を占めますが、キャンペーン時だけ「処理中」が急増したとします。ここで統計情報が古いまま(`ANALYZE`が走っていない)だとどうなるか。

PostgreSQLは「『処理中』は滅多に来ない(MCVリスト外)」と判断して、無謀なインデックススキャンを選択し、結果として数万件のランダムアクセスが発生してクエリが爆死する……という悲劇が起こります。

—

実践:MCVを確認してみる

まずは、自分のDBでどんなMCVが保持されているか見てみましょう。`pg_stats`ビューを使います。

SELECT
attname AS column_name,
most_common_vals,
most_common_freqs
FROM pg_stats
WHERE tablename = ‘orders’
AND attname = ‘status’;

`most_common_vals`(値)と`most_common_freqs`(その頻度)が配列で返ってきます。これを見て、「あ、今この値がこれくらいの割合で見積もられているんだな」と把握するだけでも、トラブルシューティングの精度がグッと上がります。

—

現場で役立つチューニングのコツ

もしクエリが遅くて、MCVが原因だと疑わしい場合は、以下のステップを試してみてください。

1. まずは `ANALYZE` を疑え: 基本中の基本ですが、テーブルのデータが激しく入れ替わった直後なら、まずは統計情報の更新です。
2. ヒストグラムの限界を知る: MCVは「特定の固定値」には強いですが、範囲検索やMCVに入り切らない値には「ヒストグラム」という別の統計情報が使われます。もし値の種類が膨大なら、`ALTER TABLE … SET STATISTICS` で統計情報の精度を上げることも検討しましょう。

  • 例:`ALTER TABLE orders ALTER COLUMN status SET STATISTICS 1000;`
  • デフォルトは100ですが、これを上げるとMCVリストの枠が増えます。ただし、`ANALYZE`の負荷も上がるので注意が必要ですよ。

3. 極端に偏ったデータにはインデックスを工夫する: 全体の9割が「完了」のステータスにインデックスを貼っても、オプティマイザは使ってくれません(使わない方が速いからです)。「未処理」など、特定の希少な値だけを検索対象にするなら、部分インデックス(Partial Index)が最強の解決策になります。

— 「未処理」だけを狙い撃ちにする最強のインデックス
CREATE INDEX idx_orders_pending ON orders (status) WHERE status = ‘pending’;

—

最後に:データベースと「対話」しよう

「DBが賢いから勝手にやってくれるだろう」と丸投げしていると、いつか大きなしっぺ返しを食らいます。でも、こうやって統計情報の裏側を覗いてみると、データベースがどう考えてクエリを処理しているかが見えてきて、少し愛着が湧きませんか?

クエリチューニングは、いわばデータベースとの対話です。「お前、この値がこんなに多いって勘違いしてないか?」と問いかけ、必要なら統計情報の精度を調整したり、インデックスを最適化してあげる。

現場のエンジニアとして、ぜひ今日から `pg_stats` を覗く習慣をつけてみてください。きっと、これまでとは違う景色が見えるはずですよ。

それでは、良いチューニングライフを!

コメント

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