なぜPostgreSQLは「ヒント句」をくれないのか?:実行計画を制御するための現場的アプローチ
「あー、このクエリ、なんでわざわざ遅いインデックス選んでるんだよ……!」
PostgreSQLを触っていると、一度はこう叫びたくなりますよね。OracleやMySQLに慣れていると、「なんで『`/+ INDEX(…) /`』みたいなヒント句がないんだ!」と憤る気持ちもよくわかります。
でも、PostgreSQLの設計思想は「オプティマイザを信じろ、統計情報を正しく育てろ」というもの。とはいえ、本番環境で「統計情報の更新を待ってくれ」なんて言ってられない緊急事態もありますよね。
今日は、そんな現場の駆け込み寺となる「実行計画の制御テクニック」について、少し裏側の話も交えてお話しします。
—
1. なぜ「ヒント句」が標準ではないのか
まず前提として、PostgreSQLにヒント句がないのは「怠慢」ではなく「ポリシー」です。
ヒント句を多用すると、データの分布が変わったときに「過去の最適解」が「未来の足かせ」になることがあります。ベンダーロックインならぬ「ヒントロックイン」ですね。一度埋め込んだヒントは、コードを修正するまで永続的にクエリを縛り続けます。
PostgreSQLは、あくまで「統計情報が正しければ、オプティマイザは常に最善を選ぶ」というスタンスをとっています。つまり、実行計画が悪いのはクエリのせいではなく、統計情報が現場のリアルを反映できていないせいだ、という考え方です。
—
2. 実践編:統計情報を「ハック」する
ヒント句がない以上、まずは「統計情報を調整して、オプティマイザを誘導する」のが王道です。
例えば、特定のインデックスを使ってほしいのに使ってくれない場合。`pg_stats` を見て、該当カラムのヒストグラムや相関関係が実態とズレていないか確認しましょう。
手法:カラムの相関関係を教え込む
統計情報が単一カラム単位だと、`WHERE a = 1 AND b = 2` のような複合条件の選択率を見誤ることがよくあります。そんなときは、拡張統計情報(Extended Statistics)の出番です。
— 複数カラムの相関を統計情報に含める
CREATE STATISTICS stats_ab ON a, b FROM my_table;
ANALYZE my_table;
これだけで、オプティマイザが「あ、この2つはセットで絞り込まれるんだな」と理解して、適切なインデックスを選んでくれるようになることが多々あります。まずはここから試すのがプロの作法です。
—
3. どうしてもダメな時の切り札:pg_hint_plan
「理屈はわかった。でも今すぐ直さないとサービスが落ちるんだ!」という時もありますよね。そんな時に頼れるのが外部拡張の `pg_hint_plan` です。
これはOracleライクな構文をPostgreSQLに持ち込む強力なツールです。
導入と使用例
まずはインストールして、設定を有効化します。
— 拡張の有効化
CREATE EXTENSION pg_hint_plan;
— クエリにヒントを埋め込む
/+
SeqScan(t1)
Leading(t1 t2)
/
SELECT FROM t1 JOIN t2 ON t1.id = t2.t1_id;
ここでの注意点:
このヒント句は、コメントとして書くので、構文エラーにはなりません。もし `pg_hint_plan` が入っていない環境で実行すると、ただの無視されるコメントになります。つまり、「動けばラッキー、動かなくてもクエリは死なない」という安全装置が働きます。
ただし、これを本番環境で乱用するのは「技術的負債の借金」です。必ず「なぜヒントが必要だったのか」をチケットに書き残し、後で統計情報の改善やクエリの書き換えで見直すタスクをバックログに入れておきましょう。
—
4. 「クエリの形」を変えるという荒技
統計情報をいじらず、ヒント句も使わずに制御する、僕が一番好きな手法がこれです。
オプティマイザを惑わせるような複雑な条件式をバラす、あるいは「強制的に結合順序を固定する」ために、わざとサブクエリを挟んだり `OFFSET 0` を使ったりします。
— オプティマイザに「ここで一度計算を区切ってくれ」と意思表示する
SELECT FROM (
SELECT FROM heavy_table WHERE condition = true OFFSET 0
) AS sub
JOIN other_table ON sub.id = other_table.id;
`OFFSET 0` は、オプティマイザに対する「最適化の境界」としてのヒントになります。これを使うと、サブクエリ内での最適化が完了した結果を結合に回せるため、無茶な結合順序になるのを防げます。
—
まとめ:現場でどう立ち回るか
最後に、先輩からのアドバイスです。
1. まずは `EXPLAIN (ANALYZE, BUFFERS)` を見て、何がズレているか特定する。
2. 統計情報が古いなら `ANALYZE` する。分布が特殊なら拡張統計情報を使う。
3. どうしても無理なら `pg_hint_plan` を検討する。
4. 最後の手段として、クエリの構造を変えてオプティマイザにヒントを与える。
ヒント句を使うのは、いわば「外科手術」です。まずは食事療法(統計情報の適正化)で治せないか考える。それが、PostgreSQLと長く、健全に付き合っていくための秘訣ですよ。
皆さんの現場のクエリが、明日も軽快に走りますように!
コメント