【実務・中級編】 default_statistics_targetの役割 – PostgreSQL

「なんだか最近、特定のクエリだけ急に遅くなった気がする……」

そんな悩みを抱えて、Explainの実行計画を覗いてみたことはあるかい? そこでよく目にするのが、「見積もり行数(rows)」と「実際の実行行数(actual rows)」の絶望的な乖離だ。

今日は、そんな悩みを抱える君に、PostgreSQLのチューニングにおける「縁の下の力持ち」、`default_statistics_target` について話をしようと思う。

—

なぜ「統計情報」がすべてを握っているのか

PostgreSQLのクエリプランナは、いわば「超高性能な地図読み」だ。目的地(結果セット)に到達するために、どの道(インデックススキャンか、シーケンシャルスキャンか、あるいはハッシュジョインか)を通るのが一番速いかを、事前に計算する。

その計算の元データになるのが「統計情報」だ。
「このカラムにはどんな値がどれくらい入っているのか?」
「ユニークな値はどれくらいあるのか?」
これらを `ANALYZE` コマンドで集計して、システムカタログに記録しているんだね。

ここで問題になるのが、デフォルトの精度だ。PostgreSQLの `default_statistics_target` は、標準で「100」に設定されている。これは、「値の分布を100個のバケット(箱)に分けて把握するよ」という意味だ。

複雑なデータ分布には「100」じゃ足りないことがある

例えば、ECサイトの注文履歴テーブルを想像してみてくれ。
`status` カラムに「完了」が99%入っていて、「キャンセル」や「保留」が残り1%に散らばっているようなケースだ。

プランナは、「100個のバケット」という粗い粒度でデータを見ているから、たまに「この値は全体の5%くらいあるはずだ」なんて大きな勘違いをしてしまうことがある。結果として、インデックスが有効なのにシーケンシャルスキャンを選択して、クエリが爆速からノロノロ運転に転落……なんてことは、現場では珍しくないんだ。

解決策:ターゲットを上げて精度をブーストする

もし、特定のカラムで「明らかにプランナが見積もりを外しているな」と確信したら、そのカラムの統計ターゲットを上げてみよう。

やり方は簡単だ。テーブル単位、あるいはカラム単位で指定できる。

— カラム単位で精度を上げる(100から500へ)
ALTER TABLE orders ALTER COLUMN status SET STATISTICS 500;

— 変更したら忘れずにANALYZE!
ANALYZE orders;

こうすることで、PostgreSQLはそのカラムに対して500個のバケットを使って分布を解析するようになる。精度が上がれば、プランナの「勘」も鋭くなり、正しい実行計画を選ぶ確率がぐんと高まるわけだ。

実践での注意点:やみくもに上げないこと

ここで一つ、先輩からのアドバイスだ。
「じゃあ、全部のテーブルを1000とかにしておけば最強じゃない?」と思うかもしれないが、それはやめておいたほうがいい。

  • ANALYZEの負荷増大: 統計情報の収集に時間がかかるようになり、大規模テーブルではバキューム処理を圧迫する。
  • ストレージとメモリ: 統計情報自体が巨大になり、システムカタログのメモリ消費が増える。

統計ターゲットをいじるのは、あくまで「どうしてもプランナが最適解を見つけてくれない、重要なカラム」だけに限定するのが鉄則だ。`pg_stats` ビューを覗いてみて、`n_distinct`(ユニーク数)や `most_common_vals`(よく出る値)が実際のデータと乖離していないか、定期的にチェックする習慣をつけるといいよ。

まとめ

今日覚えて帰ってほしいのは、この3点だ。

1. クエリの遅延は、まず「見積もりと実際の行数のズレ」を疑え。
2. データ分布が特殊なカラムには、`SET STATISTICS` で個別に精度を調整できる。
3. ただし、広範囲にやりすぎると副作用がある。ピンポイントで狙い撃て。

データベースのチューニングは、時には職人芸に近い。でも、こうやって「なぜプランナはこう動いたのか?」という裏側を理解していれば、どんな複雑なクエリも怖くなくなるはずだ。

次は、実際に遅いクエリの `EXPLAIN (ANALYZE, BUFFERS)` を取って、見積もりのズレを確認するところから始めてみよう。何か詰まったら、またいつでも聞いてくれよな。

コメント

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