「おい、またクエリが遅いって泣きついてきたのか?」
現場でよくある光景だよな。決まって「インデックスは貼ったのに、なんで実行計画がこんなにぐちゃぐちゃなんだ!」っていうやつ。でもさ、PostgreSQLのオプティマイザが間違った道を選ぶとき、その原因のほとんどは「データの相関関係」を見誤っていることにあるんだ。
今日は、そんな泥沼から君を救い出す魔法のツール、`pg_stats_ext`について話そうと思う。これを知っているだけで、パフォーマンスチューニングの引き出しが一段深くなるはずだぞ。
—
なぜ「統計情報」が重要なのか?
PostgreSQLのオプティマイザは、賢い奴だけど万能じゃない。基本的には「列ごとの分布」を元に、「このクエリにはどのインデックスが最適か?」を計算する。
でも、こんなテーブルを想像してみてくれ。
- `city`(都市)
- `zip_code`(郵便番号)
この2つの列、明らかに相関してるだろ?「東京都」なら「100番台」の郵便番号が多いはずだ。でも、PostgreSQLのデフォルトの統計情報は「列ごとの独立性」を前提にしていることが多い。だから、この2つを条件にしたときに、オプティマイザは「掛け算」で確率を計算しちまう。「ありえないほど絞り込めるはずだ」と誤認して、無駄な全件検索を選んでしまうこともあるんだ。
そこで登場するのが `pg_stats_ext` だ
PostgreSQL 10以降、この「列間の相関」をオプティマイザに教えてやるために、`CREATE STATISTICS` という機能が強化された。そして、その中身を覗き見るためのシステムビューが `pg_stats_ext` なんだ。
1. まずは統計情報を作ってみる
まずは、相関がありそうな列に対して統計オブジェクトを作ってみよう。
CREATE STATISTICS stats_city_zip
ON city, zip_code FROM users;
これだけで、「この2つの列には関連があるぞ」という情報をオプティマイザに渡せる。
2. `pg_stats_ext` で中身を確認する
統計オブジェクトを作った後、実際にPostgreSQLがどう認識しているかを確認するのがこのビューの役割だ。
SELECT
schemaname,
statistics_name,
n_distinct,
dependencies
FROM pg_stats_ext
WHERE statistics_name = ‘stats_city_zip’;
ここで重要なのが `dependencies`(依存関係)カラムだ。ここには、列同士がどれくらい密接に関係しているかの数値が入る。もしここが「1」に近い値なら、オプティマイザは「あ、この列を組み合わせると絞り込みの精度が爆上がりするな」と判断して、正しい実行計画を選ぶようになる。
実務で「詰まった」ときのチェックリスト
実務でパフォーマンスが出ないときは、以下の手順でこのビューを叩いてみてくれ。
1. クエリの実行計画(EXPLAIN ANALYZE)を見る:
「見積もり行数」と「実際の行数」に桁違いの乖離がないか確認する。
2. `pg_stats_ext` を確認する:
そもそも統計オブジェクトが作成されているか?作成されているなら、`dependencies` が適切に算出されているか(`ANALYZE`直後であることも大事だぞ)。
3. 統計オブジェクトを最適化する:
もし複雑な式での絞り込みが多いなら、`CREATE STATISTICS … ON (expression)` を使って、計算結果に対して統計を取るのも手だ。
先輩からのアドバイス
最後に一つだけ覚えておいてほしい。「統計情報を取れば取るほど速くなるわけではない」ってことだ。
統計オブジェクトを増やしすぎると、`ANALYZE` の負荷が上がって、書き込み処理が重くなる。それに、オプティマイザが考慮すべき要素が増えすぎて、逆に最適化に時間がかかることもある。
「本当に相関が強くて、実行計画が狂いやすい場所」にだけ、ピンポイントでこの仕組みを使ってやる。これができるのが、できるエンジニアの証だ。
次は、実際に遅いクエリを持ってきて、`pg_stats_ext` を見ながら一緒に最適化してみようぜ。理論だけじゃなくて、手を動かさないと見えてこない景色があるからな。
じゃあ、今日はこの辺で。また何かあればいつでも聞きに来てくれ。
コメント