「またクエリプランナーがやらかしてくれたよ……」
そんな嘆きを夜中のデバッグ中に聞いたことはありませんか? 巨大なテーブル同士を結合するとき、PostgreSQLが「おい、この結合結果はたったの1行だぜ!」と自信満々に誤った見積もりをして、結果として Nested Loop が爆発し、クエリが永遠に終わらない……。
僕らが向き合っているのは、そんな「おせっかいな名推理」をするプランナーとの戦いです。今日は、そのプランナーに正しい「世界の見方」を教えるための魔法、「多列統計情報(Extended Statistics)」、特に N-Distinct 統計 について話をしよう。
—
なぜプランナーは「勘違い」をするのか?
まず前提として、PostgreSQLのオプティマイザは、基本的には「各列の統計情報は独立している」と仮定して計算します。
例えば、`都道府県` と `市区町村` という列があるとしよう。
「東京都」の行数は全体のうち30%、「新宿区」の行数は全体のうち1%だとプランナーが知っているとする。もし、`WHERE 都道府県 = ‘東京都’ AND 市区町村 = ‘新宿区’` という条件が来たら、プランナーはこう計算するんだ。
- 「東京都である確率(0.3) × 新宿区である確率(0.01) = 0.003(つまり全体の0.3%)」
……お気づきかな? これは明らかに間違いだよね。新宿区は東京都にしか存在しない。この2つの列には強い相関があるからだ。
プランナーは「列同士の相関関係」まではデフォルトでは知らない。だから、見積もりと実際の行数に天と地ほどの差が生まれるわけだ。
そこで登場するのが「CREATE STATISTICS」
PostgreSQL 10以降、僕らにはこの「勘違い」を正すための強力なツールが用意されている。それが多列統計情報だ。
特に今回紹介する `ndistinct` は、「複数列を組み合わせた時のユニークな値の数」をプランナーに学習させるものだよ。
実践:どうやって使うのか?
使い方は驚くほどシンプルだ。まずは、どの列の組み合わせが問題を起こしているかを見極める。EXPLAIN ANALYZE を取ってみて、見積もり(rows)と実績(actual rows)が大きく乖離している箇所を探すんだ。
見つけたら、こんな風に統計オブジェクトを作成する。
— 都道府県と市区町村の組み合わせのユニーク数を学習させる
CREATE STATISTICS stats_pref_city (ndistinct)
ON prefecture, city
FROM users;
これだけで、プランナーは「あ、この2つの列を組み合わせると、思ったよりユニークな値が少ないな(=相関があるな)」と理解してくれるようになる。
統計を反映させるための儀式
SQLを実行しただけでは、まだ古い統計情報のままだ。忘れずに `ANALYZE` を実行しよう。
ANALYZE users;
これで、プランナーの「視力」が矯正されたはずだ。もう一度クエリを投げてみてほしい。プランナーが適切な結合アルゴリズム(例えば、Nested Loop から Hash Join への変更など)を選択してくれる可能性がグッと高まるはずだ。
—
現場のエンジニアへのアドバイス
僕がこの機能を使っていて、いつも自分に言い聞かせていることがいくつかある。
1. 「何でもかんでも」作らないこと
統計オブジェクトもデータの一部だ。数が増えればそれだけ `ANALYZE` の負荷も増えるし、カタログの管理コストもかかる。「本当に遅いクエリ」を解決するために、ピンポイントで適用するのがプロの流儀だ。
2. 実行計画の変化を観察せよ
`ndistinct` は万能じゃない。依存関係が強い列には `dependencies` を使った方がいい場合もある。プランナーがどう変化したのか、`EXPLAIN` を細かく比較する癖をつけよう。
3. まずは「データの偏り」を疑え
多列統計は強力だけど、そもそも「単一列の統計」が最新でないなら、まずは `ANALYZE` を徹底するのが先決だ。基本を飛ばして魔法に頼るのは、トラブルの元だよ。
—
まとめ:プランナーと対話しよう
データベースチューニングっていうのは、結局のところ「オプティマイザとの対話」なんだ。プランナーが何を勘違いしているのかを読み解き、適切なヒント(統計情報)を与えてあげる。
そうやって調整したクエリが、それまで数分かかっていたものが数ミリ秒で返ってくるようになったとき……これだからDBエンジニアはやめられないよね。
もし君の現場で、どうしてもプランナーが言うことを聞いてくれない「頑固なクエリ」があったら、ぜひ一度 `CREATE STATISTICS` を試してみてくれ。きっと、驚くような結果が待っているはずだ。
それじゃ、また現場で会おう。健闘を祈るよ。
コメント