PostgreSQLの「プランナの勘違い」を正す――結合制御フラグとの上手な付き合い方
現場でデータベースを触っていると、たまに「なんでオプティマイザはこんな変な実行計画を選んだんだ?」と頭を抱える夜がありますよね。
特に、数千万行のテーブル同士を結合(JOIN)する際、本来ならハッシュ結合でサクッと終わらせてほしいのに、なぜかネステッドループを延々と回してクエリが帰ってこない……なんて経験、一度はあるはずです。
PostgreSQLのプランナは非常に優秀ですが、統計情報の鮮度が落ちていたり、複雑な相関関係があったりすると、たまに「選択肢を読み違える」ことがあります。そんな時、僕たちが介入するための強力なツールが、結合手法を強制的に制御する「フラグ」たちです。
今日は、実務でこの「最後の手段」をどう扱うか、その勘所を解説します。
—
結合制御の「三種の神器」
PostgreSQLには、特定の結合アルゴリズムを無効化するための設定パラメータが用意されています。
- `enable_nestloop`: ネステッドループ結合を制御。
- `enable_mergejoin`: マージ結合を制御。
- `enable_hashjoin`: ハッシュ結合を制御。
これらを `SET enable_hashjoin = off;` のように設定すると、プランナはその手法を「選択肢から外して」計画を作成します。
実務での使いどころ:いきなり「オフ」にするのはNG
まず大切な心構えから。これらのフラグを本番環境のグローバル設定(`postgresql.conf`)でいじるのは絶対に厳禁です。
これをやると、プランナが「本来なら最適なはずの選択肢」さえ選べなくなり、システム全体のパフォーマンスが雪崩のように崩壊する可能性があります。
僕がこれらを使うのは、あくまで「調査」と「一時的な回避」のときだけです。手順としてはこうです。
1. EXPLAIN ANALYZEで現実を確認する:何が起きているか、実際のコストと実行時間を見る。
2. 一時的にOFFにして比較する:トランザクション内だけで特定のフラグをオフにし、実行計画がどう変わるか、時間は短縮されるかを確認する。
— トランザクション内で実行計画の変化を確認する
BEGIN;
SET LOCAL enable_nestloop = off; — 一時的にネステッドループを禁止
EXPLAIN ANALYZE SELECT FROM users u JOIN orders o ON u.id = o.user_id;
ROLLBACK;
こうすれば、他のセッションに影響を与えずに、プランナの挙動をコントロールできます。
なぜプランナは「間違える」のか?
ここが一番面白いところなんですが、実はプランナが間違えているのではなく、僕たちが提供している「統計情報」が間違っていることがほとんどです。
例えば、`WHERE`句で絞り込んだ結果、実は数件しか残らないとわかっているのに、統計情報上は「数万件ある」と判断されていれば、プランナは「全件走査(シーケンシャルスキャン)」を避けようとしてネステッドループを選択し、結果として非効率になることがあります。
もし、特定のフラグをオフにしてパフォーマンスが劇的に改善したなら、それは「プランナが間違ったのではなく、統計情報が実態と乖離している」というサインです。
現場の先輩からのアドバイス
「フラグをいじって速くなった!」で満足してはいけません。それはあくまで応急処置です。その後にやるべきことは一つ。
「なぜプランナはそれを選んだのか? 統計情報を正すにはどうすればいいか?」を考えることです。
- `ANALYZE` を実行して統計情報を更新する。
- 相関関係があるカラムなら、`CREATE STATISTICS` で拡張統計情報を作成する。
- それでもダメなら、クエリの書き方を少し変えて、プランナが理解しやすいヒントを与える。
フラグをいじるのは、あくまで「何が最適か」を見極めるための実験道具として使ってください。この感覚さえ持っていれば、どんなに巨大なデータベースを相手にしても、落ち着いてボトルネックを追い込めるようになります。
—
データベースのチューニングは、プランナという優秀な「助手」との対話です。彼が迷っているときは、少しだけヒント(制御フラグ)を出して、正しい道へ導いてあげましょう。
それでは、また現場で会いましょう。クエリの実行計画が、常に理想的であることを祈っています!
コメント