「クエリが急に遅くなった。インデックスも貼ってあるし、`EXPLAIN ANALYZE`しても実行計画は悪くなさそうなのに……」
データベースエンジニアをやっていると、こんな壁にぶつかることは一度や二度じゃないはずです。そんな時、多くの人はインデックスの追加や書き換えに走りがちですが、ちょっと待ってほしい。
PostgreSQLが「どうやってそのクエリを実行するか」を決める際、何を頼りにしているか知っていますか? それが「統計情報」です。
今日は、PostgreSQLのオプティマイザの「目」である`pg_stats`ビューについて、現場でどう使いこなすべきか、ちょっと深掘りして解説します。
—
なぜオプティマイザは「勘違い」するのか?
PostgreSQLのクエリオプティマイザは、テーブルの行数やデータの分布を見て、最適なプラン(インデックスを使うか、フルスキャンするかなど)を決定します。この時、テーブルの中身を全部スキャンするわけにはいかないので、`ANALYZE`コマンドによって収集された「要約データ」を参考にします。
それが`pg_stats`です。
もしクエリの推定行数と実際の行数が大きく乖離しているなら、統計情報が実態とズレている可能性が高い。そんな時、`pg_stats`を覗くと原因が一発で分かることが多いんです。
まずは現状を確認してみよう
例えば、`users`テーブルの`status`カラムでパフォーマンス問題が起きていると仮定します。まずは、どんな統計が取られているか見てみましょう。
SELECT
attname, — カラム名
null_frac, — NULL値の割合
n_distinct, — 推定カーディナリティ
most_common_vals, — 頻出値(MCV)
most_common_freqs — 頻出値の出現頻度
FROM pg_stats
WHERE tablename = ‘users’ AND attname = ‘status’;
ここで注目すべきは以下の3点です。
- `null_frac`: これが高いのにクエリで`IS NOT NULL`を多用していると、推定が狂いやすい。
- `most_common_vals` (MCV): 頻繁に出現する値のリストです。ここに含まれていない値で検索すると、オプティマイザは「平均的な分布」を前提に計算を始めます。これが悲劇の始まりです。
- `n_distinct`: ユニークな値の数。ここが極端にずれていると、オプティマイザは「この条件なら1行しか返らないはずだ(インデックスを使おう!)」と誤認し、実際には全件スキャンが必要なほど重いクエリにインデックスを適用して爆死する、なんてことが起きます。
ヒストグラムを読み解く「コツ」
`pg_stats`には`histogram_bounds`という配列も存在します。これは、データがどのような範囲で分布しているかを示す指標です。
もし特定の期間に大量のレコードが挿入される運用をしているなら、`ANALYZE`のタイミングと実態にズレが生じます。`histogram_bounds`を眺めてみて、「最近追加されたIDの範囲がここに入っていないな」と気づければ、手動で`ANALYZE`を打つという判断が即座にできますよね。
こんな時は「統計情報の更新」か「型の見直し」を
現場でよくあるのが、「特定の値だけが異常に多い(偏った分布)」ケースです。
例えば、`status`が`’active’`ばかりで、`’archived’`が極端に少ない場合。オプティマイザはMCV(頻出値)を見て賢く判断しますが、統計情報の精度が低い(`default_statistics_target`の設定が低い)と、この偏りを正しく捉えられません。
そんな時は、まず統計情報の精度を上げてみましょう。
— 特定のカラムだけ統計精度を上げる
ALTER TABLE users ALTER COLUMN status SET STATISTICS 1000;
ANALYZE users;
これだけで、今までフルスキャンを選択していたクエリが、インデックスを使うようになることも珍しくありません。
最後に:ツールに頼りすぎないための「勘」
`pg_stats`は強力な武器ですが、あくまで「過去のデータのスナップショット」に過ぎません。
「推定行数(rows)と実際の行数(actual rows)がどれくらいズレているか」を`EXPLAIN ANALYZE`で追いかける癖をつけること。そして、そのズレの理由が「統計情報の古さ」なのか、「データの極端な偏り」なのかを、`pg_stats`で答え合わせする。
この往復運動ができるようになると、データベースエンジニアとしての腕は一段階上がります。
クエリが遅いと嘆く前に、まずはオプティマイザが何を見ているのか、`pg_stats`を通して「視点」を共有してあげてください。きっとPostgreSQLも、期待に応えてくれるはずですよ。
それでは、また現場で!
コメント