「なぜかクエリが遅い」を解決する第一歩。PostgreSQLの `null_frac` と付き合う技術
エンジニアのみなさん、お疲れ様です。
データベースのパフォーマンスチューニングをしていると、必ずと言っていいほど直面するのが「クエリプランの読み違え」です。オプティマイザが「このクエリは高速だ」と判断したのに、実際にはインデックスが無視されてフルスキャンが走っている……そんな経験、一度はありますよね。
特に、NULL値が混在するカラムでその現象が起きているなら、犯人は統計情報の `null_frac`(NULL率) かもしれません。
今日は、この地味だけど超重要なパラメータと、どうやって付き合っていくべきかについて、実務的な話をしようと思います。
—
`null_frac` ってそもそも何者?
PostgreSQLはクエリを実行する前に、統計情報を元に「どのくらいの行がヒットするか」を予測します。この「ヒット率(選択率)」の見積もりに使われるのが `pg_stats` ビューにある `null_frac` です。
`null_frac` は、「そのカラムにNULLがどれくらいの割合で含まれているか」を0から1の値で示したものです。
もし、あるカラムにNULLがめちゃくちゃ多いのに、オプティマイザが「ここはほとんどNULLじゃないはずだ」と勘違いしていたらどうなるか。当然、インデックススキャンではなく、効率の悪いテーブルスキャンを選択してしまいます。
実践:こんなところで落とし穴がある
例えば、こんなテーブルがあるとしましょう。
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
user_id INT,
coupon_code VARCHAR(20) — 使われていない時はNULL
);
「クーポンを使った注文だけを抽出したい」というクエリを投げるとします。
SELECT FROM orders WHERE coupon_code IS NOT NULL;
もし、`coupon_code` が全体の99% NULLで、残り1%しか値が入っていないとします。このとき、PostgreSQLが統計情報を最新の状態に保てていれば、オプティマイザは「あ、これはごく一部のデータを取り出すんだな」と判断してインデックスを使ってくれます。
しかし、もしデータが急激に増減して、統計情報が古いままだったら?あるいは、統計情報の更新が追いついていない特殊な分布だったら?オプティマイザは「これ、結構な量があるはずだ」と誤認し、全行スキャンを始めてしまうわけです。
統計情報を確認してみよう
現場で「プランがおかしいな?」と思ったら、まずは今の統計情報を疑いましょう。
SELECT tablename, attname, null_frac, n_distinct
FROM pg_stats
WHERE tablename = ‘orders’ AND attname = ‘coupon_code’;
ここで表示される `null_frac` の値が、実際のデータと乖離していないかチェックするんです。もし乖離が激しいなら、まずは `ANALYZE` ですね。
ANALYZE orders;
これだけで解決することも多いですが、それでもダメな場合は、統計情報の精度を上げるために「統計情報の収集対象」を調整したり、カラムごとのステータスを細かく設定したりする必要があります。
どうしても統計情報が上手くいかない時
現場では「`ANALYZE` をかけても、どうしても分布が複雑で上手く見積もれない」というケースもあります。そんな時は、プランナにヒントを与えるというアプローチもアリです。
- 定数によるフィルタを工夫する: 特定の値が多いことがわかっているなら、クエリ自体に条件を追加して絞り込む。
- 統計ターゲットを上げる: 特定のカラムだけ統計情報の精度を上げたいなら、以下のように設定します。
ALTER TABLE orders ALTER COLUMN coupon_code SET STATISTICS 1000;
ANALYZE orders;
`STATISTICS` の値を上げると、より詳細なサンプリングが行われます。その分、`ANALYZE` の時間はかかるようになりますが、複雑なデータ分布を持つカラムには劇的に効くことがあります。
今日のまとめ:エンジニアとしての心得
僕がデータベースを触る時に大切にしているのは、「統計情報は生き物だ」と考えることです。
コードを書くとき、NULLを許容するかどうかは設計の段階で決まりますが、その後の「データの偏り」は運用でしか分かりません。クエリが遅いと文句を言う前に、統計情報が現実のデータを正しく捉えているか、`pg_stats` を覗いてみる。そんな「データベースとの対話」を楽しめるようになると、チューニングの腕は格段に上がります。
もし皆さんの現場で、「なぜかインデックスが効かないクエリ」を見つけたら、まずは `null_frac` を疑ってみてください。きっと、そこには意外な真実が隠れているはずですよ。
それでは、また次回の記事でお会いしましょう!
コメント