統計情報の「顔」を知る:PostgreSQLのMCVリストとクエリオプティマイザの駆け引き
現場でパフォーマンスチューニングをしていると、必ずと言っていいほど「なぜプランナはこんな突拍子もない実行計画を選んだんだ?」という壁にぶつかります。
多くの場合、犯人は統計情報の不一致です。特に、カラムのデータ分布が偏っている場合、PostgreSQLのオプティマイザは「MCV(Most Common Values)」という強力な武器を頼りにしますが、これが時として諸刃の剣になることを皆さんはご存知でしょうか。
今日は、PostgreSQLの内部アーキテクチャの心臓部の一つであるMCVリストについて、教科書には載っていない「現場の裏側」を掘り下げてみたいと思います。
MCVリストとは何か、そしてなぜ「特別」なのか
PostgreSQLの `ANALYZE` コマンドを実行すると、システムカタログ `pg_statistic` にデータ分布のサマリが書き込まれます。その中で、MCVリストは「最も頻出する値」とその「出現頻度」をペアで保持する、極めて重要なメタデータです。
オプティマイザはクエリを評価する際、`WHERE` 句の条件がどれくらいの行数(カーディナリティ)にヒットするかを見積もります。もし、検索対象がMCVリストに含まれている値であれば、プランナは計算を放棄して、そのリストから正確な頻度を引っ張ってくる。これが、統計情報の恩恵です。
しかし、ここからが本題です。
「分布の偏り」が引き起こす見積もりの崩壊
もしデータ分布がフラットであれば、`histogram_bounds`(ヒストグラム)だけで十分です。しかし、世の中のデータは残酷なほど偏っています。
例えば、あるECサイトの注文履歴テーブルで「ステータス」カラムを考えてみてください。
- `COMPLETED`: 95%
- `PENDING`: 4%
- `CANCELLED`: 1%
このとき、`status = ‘CANCELLED’` という条件でクエリを投げたとします。MCVリストが正しく機能していれば、プランナは「あ、これ全体の1%だな。よし、Index Scanでいこう」と判断します。
問題は、この 「MCVリストの鮮度」と「ヒストグラムとの境界線」 です。
1. 死角を突くクエリ: MCVリストに載っていない値、あるいはヒストグラムの境界線ギリギリの値で検索された場合、プランナは「その他大勢(n_distinct)」として確率を均等配分して見積もります。これが、数桁単位の行数見積もりミスを生む最大の要因です。
2. 統計情報の更新ラグ: `autovacuum` が走るタイミングと、実際のデータ更新の激しさの間に乖離があると、MCVリストは「過去の亡霊」になります。この亡霊を信じてプランナがNested Loopを強行し、数百万行を全件スキャンするハメになる……なんて悲劇は、どの現場でも一度は経験するはずです。
トラブルシューティング:プランナを正気に戻すには
もし、クエリの見積もりが明らかに狂っているなら、まずは `EXPLAIN ANALYZE` を叩いてください。`Rows Removed by Filter` と `Actual Rows` の差を見るのです。
もしここが乖離しているなら、以下の手順を試します。
- 統計対象の分解能を上げる:
`ALTER TABLE … ALTER COLUMN … SET STATISTICS 1000;`
デフォルトの100では足りないケースが多々あります。特にIDやステータスのようなキーカラムには、迷わず高い値を設定しましょう。
- 多変量統計(Extended Statistics)の活用:
「AカラムとBカラムの組み合わせでデータが偏る」という場合は、MCVリスト単体では太刀打ちできません。`CREATE STATISTICS` を使い、カラム間の相関関係をプランナに教え込むのです。PostgreSQL 10以降、これは必須の教養と言えます。
- 極端な偏りへのアプローチ:
あまりに特定の頻出値が多い場合は、そもそもインデックスを貼るべきか、あるいは `PARTITIONING` で物理的にデータを分離すべきかを検討すべきです。統計情報に頼りすぎる設計は、データベースが巨大化するほど脆くなります。
最後に:プランナは「優秀な参謀」だが「魔法使い」ではない
私たちが書くSQLは、あくまで「何を」欲しいかを伝えるだけで、「どうやって」取得するかはプランナに委ねられます。しかし、プランナの判断材料である統計情報(MCVリストなど)を整えるのは、我々エンジニアの責任です。
「なぜか遅い」と感じたとき、クエリの書き方をいじる前に、一度 `pg_stats` を覗いてみてください。そこに、データベースが考えている「現実と、あなたのデータの現実」のギャップが隠れています。
データベースのチューニングは、データと対話する行為です。MCVリストという「データの顔」を知ることは、PostgreSQLとより良い関係を築くための第一歩だと、私は信じています。
それでは、また次回の深掘りでお会いしましょう。
コメント