「なんだか最近、特定のクエリだけ急に遅くなった気がする……」
そんな悩みを抱えて、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)` を取って、見積もりのズレを確認するところから始めてみよう。何か詰まったら、またいつでも聞いてくれよな。
コメント