クエリ最適化 — 同じ結果でも「解き方」で速さが激変する
SQL は「何が欲しいか」だけを書く宣言的な言語。じゃあ「どうやって取り出すか」は誰が決める? その仕事人がオプティマイザです。同じ問い合わせに何通りもある実行計画のコストを見積もり、一番安いものを選ぶ。その舞台裏を動かして覗きます。
1. 実行計画は「演算子の木」
DBMS は SQL を、スキャン → フィルタ → 結合 → 集約といった演算子(オペレータ)を積み上げた木に変換します。データは葉(表スキャン)から根(結果)へ向かって流れ、途中で絞られたり合体したりします。
ポイントは、同じ SQL でも木の形は何通りもあること。下のボタンで「フィルタを結合の前に置く計画A」と「結合してからフィルタする計画B」を切り替え、各枝を流れる行数とコストの差を見てください。
実行計画の木 — 行が葉から根へ流れる
青い点=流れる行。枝の太さと数字はその枝を通る行数を表します。計画A(プッシュダウン)は結合に入る行が少ないので赤い太枝が消え、コスト(=各演算子が出力する行数の合計)がぐっと下がります。
POINT — 宣言的だから最適化できる
SQL は手順ではなく「欲しい結果の条件」を書く。手順(実行計画)を選ぶ自由がオプティマイザに残るから、DBMS はデータ量や索引に応じて毎回一番速いやり方を選べる。手続き型で手順を固定してしまうと、この最適化の余地は消える。
コスト ≈ Σ(各演算子が処理する行数)
実際は CPU・I/O・メモリを重み付けした推定値だが、「処理する行数の合計」がその一番の骨格
2. 述語プッシュダウン — フィルタを結合の前へ
最大の勝ち筋が述語プッシュダウン。フィルタ(WHERE 条件)を、結合より手前に押し込むテクニックです。結合は入力の行数が多いほど重いので、結合する前に行を減らしておくと劇的に速くなります。
下のパイプで、フィルタの位置を結合の前後で切り替えてみてください。結合が飲み込む行数が桁違いに変わります。
パイプで見る述語プッシュダウン
パイプの太さと点の密度=流れる行数。フィルタを先に置くと結合に入る流れが細くなり(緑)、後に置くと結合が 1 万行を丸ごと処理します(赤)。下から入る customers は結合相手の表です。
注意 — 遅いフィルタは巨大な中間結果を生む
結合してからフィルタすると、いったん1 万行の中間結果をメモリに作ってから 200 行まで捨てることになる。捨てるために作る、という無駄。だから多くの DB は自動でプッシュダウンを試みるが、関数でくるんだ列や複雑な副問い合わせでは押し込めないことがある。
3. コストを見積もって一番安い計画を選ぶ
オプティマイザは候補の計画それぞれについてコストを見積もり、最小のものを選びます。見積もりの鍵は選択率(フィルタを通り抜ける行の割合)。統計情報(ヒストグラムなど)から推定します。
スライダーで選択率を動かし、2 つの計画のコスト(積み上げ棒)と、オプティマイザがどちらを選ぶかを見てください。
選択率とコスト — オプティマイザの天秤
積み上げ棒の内訳=各演算子のコスト(水色=orders走査 / 青=customers走査 / 橙=フィルタ / 赤=結合 / 緑=集約)。★ が付いた方がオプティマイザの選択。選択率が小さいほど、プッシュダウンした計画Aの赤い結合が縮み、差が開きます。
出力行数 ≈ 入力行数 × 選択率
選択率の推定が外れる(統計が古い等)と、計画の見積もりも外れて「なぜか遅い」が起きる
4. 全表スキャンか、索引スキャンか
もう一つの古典的な判断がアクセス方法の選択。少数の行だけ欲しいなら索引スキャンが速い。でも大部分の行が該当するなら、飛び飛びに索引をたどるより全表を一気読みした方が速いこともあります。ここでも決め手は選択率です。
選択率で逆転する — スキャン方法のコスト曲線
橙=全表スキャン(該当が何行でも一定コスト)、水色=索引スキャン(該当行が増えるほど、飛び飛びアクセスで割高に)。2 本が交差する点より左では索引、右では全表が安い。だから同じ列でも条件次第で選ぶ方法が変わります。
POINT — 索引はいつでも速い、ではない
「索引を張ったのに使われない」の多くは、オプティマイザが正しく「その条件では全表スキャンの方が安い」と判断した結果。ヒットする行が多すぎるとき、索引は逆に遅い。速さは絶対ではなく選択率とデータ量で決まる。
5. まとめ — オプティマイザという名の職人
- 実行計画:SQL は演算子の木に変換され、同じ結果に何通りもの木がある。
- 述語プッシュダウン:フィルタを結合の前に押し込み、重い結合が扱う行数を減らす最大の勝ち筋。
- コスト見積もり:選択率(統計情報から推定)を使って各計画の処理行数を見積もり、最小を選ぶ。
- アクセス方法:全表スキャンと索引スキャンは選択率で逆転する。索引が常に速いわけではない。
一歩先へ — 結合順序の爆発と、統計の重み
表が n 個の結合は、順序の組み合わせが階乗的に爆発する(10 個で数百万通り)。だから現実のオプティマイザは動的計画法や遺伝的探索で「良い順序」を効率よく探す。そして全ての土台が統計情報だ。ヒストグラムが古いと選択率の推定が外れ、最適なはずの計画が最悪になる。
ANALYZE で統計を更新し、EXPLAIN で実際に選ばれた計画を覗く——これがチューニングの第一歩になる。