「enable_xxx」という劇薬:PostgreSQLのプランナを飼いならすということ
DBエンジニアとして現場を渡り歩いていると、たまに「どうしてもこいつ(プランナ)の言うことを聞かせたい」という場面に出くわしますよね。
統計情報は最新、インデックスも適切、相関関係も計算済み。それなのに、なぜかプランナは深夜のバッチ処理で、数百万件のテーブルに対して律儀にフルスキャン(Seq Scan)を選択し、メモリを食いつぶしに行く。そんな「空気が読めないプランナ」に手を焼いた経験、一度はあるはずです。
そこで禁断の果実のように登場するのが、`enable_seqscan` や `enable_indexscan` といった実行計画制御パラメータです。今日は、これらを単なる「スイッチ」としてではなく、PostgreSQLの内部構造とどう対話するのかという観点で掘り下げてみます。
なぜ、プランナは「間違える」のか
まず大前提として、これらのパラメータは「機能の無効化」ではなく、あくまで「コスト見積もりに対する重み付け」であることを理解しておく必要があります。
PostgreSQLのプランナは、コストベース最適化(CBO)エンジンです。膨大な組み合わせの中から、最も低コストと思われる実行計画を導き出します。しかし、コスト計算式はあくまで近似値です。統計情報の粒度(`default_statistics_target`)や、データ分布の歪み、あるいは複雑な結合条件などが重なると、プランナは「現実にはあり得ない安価なコスト」を算出し、地雷を踏むことがあります。
制御パラメータは「最後の手段」
`SET enable_hashjoin = off;` といったコマンドを叩けば、確かにその計画を強制的に排除できます。しかし、これには重大なリスクが伴います。
- グローバル/セッション設定の罠:
つい「特定のクエリのために」セッション設定でオフにした結果、そのコネクションで実行される他の全く関係ないクエリまで、不適切な実行計画を強要されることになります。
- 将来の破壊:
今のデータセットでは「Hash Joinを避けるのが正解」でも、データ量が10倍になった未来では、Hash Joinこそが最適解かもしれません。その時、この設定は「技術的負債」となってシステムを内側から食い荒らします。
だからこそ、私はこう断言します。「enable_xxx系を本番環境で気軽にいじるな。やるなら、それ以外の全ての手段を尽くした後だ」と。
現場で「プランナを導く」正しいステップ
もし、特定のクエリがどうしても遅い場合、まずはパラメータをオフにする前に、以下の「対話」を試みてください。
1. `EXPLAIN (ANALYZE, BUFFERS)` の深層を読む:
実行計画の「Estimated Cost」と「Actual Cost」の乖離を見てください。どこで見積もりが外れているのか。それは統計情報の不備か、あるいは相関サブクエリのコスト評価ミスか。
2. 統計情報の微調整:
特定のカラムに傾斜があるなら、`CREATE STATISTICS` で多変量統計を作成し、プランナにヒントを与えてください。これだけで、`enable_xxx` を触らずに解決するケースが多々あります。
3. プランヒントの検討(拡張機能):
どうしても制御が必要なら、PostgreSQL標準のパラメータをいじるよりも、`pg_hint_plan` のような拡張機能の導入を検討すべきです。これなら、特定のクエリに対してのみ、かつコードレベルで実行計画を固定できるため、他のクエリへの副作用を最小限に抑えられます。
それでも「enable_xxx」を使うとき
もちろん、避難用ハッチとしてこれらのパラメータが用意されていることには感謝すべきです。例えば、PostgreSQLのバージョンアップ直後、急激なプランの変化によってサービスが停止するような緊急事態。そんな時、とりあえずの応急処置として `enable_seqscan = off` を叩くのは、エンジニアとしての正しい判断です。
ただし、「その設定は消すことが前提である」という付箋を、チケットシステムやコードのコメントに大きく残しておいてください。
最後に
PostgreSQLのプランナは、非常に優秀で、かつ頑固な職人です。彼を無理やりねじ伏せようとすると、どこかで必ず歪みが生じます。
パラメータをオフにするのは、職人に命令を下すのではなく、職人に「なぜそこが遅いのか」を理解させるためのヒントを与えることだと考えてみてください。そうやって根気強くデータベースと向き合うことこそが、真のデータベースエンジニアの醍醐味ではないでしょうか。
さて、今夜もログを眺めながら、プランナと対話するとしますか。
コメント