PostgreSQL のプランナーをコントロール—pg_plan_advice の役割

PostgreSQL のクエリプランナーは多くの場合、優れた判断を下します。しかし時には、ユーザーが「このプランの方が良い」と判断したい場面も存在します。

pg_plan_advice は、プランナーの決定を記録し、その判断を部分的に、あるいは完全に上書きするための仕組みを提供する モジュールです。

重要な点として、プランナーの判断を無視することは危険を伴う可能性があります。データの分布が変わると、本来はプランナーが自動的に計画を調整して性能を保つ機能が、プラン助言によって阻害されてしまう場合があるからです。つまり、このモジュールは「制約のメリットが、リスクを上回る場合にのみ使用する」という慎重な姿勢が重要です。

プランナーの判断を上書きできるが、その判断を失うことの代償を常に意識する必要がある。

実装と使用方法—EXPLAIN から助言文字列を生成

モジュールを有効にするには、以下の3つの方法があります。

  • システム全体での有効化:shared_preload_libraries に pg_plan_advice を追加してサーバーを再起動
  • セッション単位での有効化:session_preload_libraries に追加して新しいセッションを開始
  • 個別セッション:LOAD コマンドで動的にロード

モジュールが読み込まれると、EXPLAIN コマンドに新しい PLAN_ADVICE オプションが追加されます。これを使うと、プランナーが実際に選択したプランを「助言文字列」の形で確認できます。

具体例として、以下のクエリを実行すると:

EXPLAIN (COSTS OFF, PLAN_ADVICE)
  SELECT * FROM join_fact f 
  JOIN join_dim d ON f.dim_id = d.id;

プランナーが選択した結果が、以下のような助言形式で表示されます:

JOIN_ORDER(f d)
HASH_JOIN(d)
SEQ_SCAN(f d)
NO_GATHER(f d)

これらの記号は以下を意味します:

記号 意味
JOIN_ORDER(f d) テーブル f を駆動表として、最初に d と結合
HASH_JOIN(d) d をハッシュジョインの内側に配置
SEQ_SCAN(f d) f と d は両方ともシーケンシャルスキャンで取得
NO_GATHER(f d) f と d は並列処理(Gather/Gather Merge)の下に現れない

生成された助言文字列は、プランナーの意思決定を「再現可能な形」で記録する。

実務的には、システムが生成した助言文字列から、自分たちが制御したい部分だけを抜き出して使うことが推奨されています。たとえば、結合順序だけを固定したい場合:

SET pg_plan_advice.advice = 'JOIN_ORDER(f d)';
EXPLAIN (COSTS OFF)
  SELECT * FROM join_fact f 
  JOIN join_dim d ON f.dim_id = d.id;

このように設定すると、EXPLAIN の出力に /* matched */ という フィードバックが表示され、助言が正しく適用されたことを確認できます。個人的には、この「段階的に助言を追加する」アプローチは賢明だと感じます。一気にプランを固定するのではなく、必要な部分だけを慎重に制御する設計になっているのは、PostgreSQL コミュニティの慎重さを示しているように思います。

何が変わるのか—プラン安定化と実験のバランス

このモジュールの登場で、以下の2つのユースケースが実現できるようになります:

  1. プラン安定化:本番環境で「このプランで安定している」という計画を明示的に記録し、将来のデータ分布変化によって意図しない別のプランが選ばれることを防ぐ
  2. 実験的な最適化:異なる結合順序やスキャン方式を試してパフォーマンスを比較し、プランナーが見落とした可能性を探る

従来は、これらの制御は enable_* パラメータ(enable_hashjoin など)で粗く行うか、統計情報の操作、あるいは全文的にプランナーの判断に頼るしかありませんでした。本来、PostgreSQL のプランナーは統計情報に基づいて合理的な判断を下すのが原則です。しかし、統計情報が不完全だったり、ドメイン知識がプランナーに伝わっていない場合、人間の介入が必要になる場面もあります。

pg_plan_advice は、その介入を「記録可能で、再現可能で、部分的」にする仕組みです。これにより、経験則と データドリブンなアプローチの間に中道を提供するモジュールといえます。

実装時の注意点と今後の活用

使用時に気をつけるべき点として、公式ドキュメントでも強調されています。

「プランナーはしばしば良い判断を下すため、その判断を上書きすることは容易に裏目に出る可能性がある」

特に注意が必要なケースは:

  • 助言によってプランが固定され、データ分布変化に追従できなくなる
  • 助言文字列が古いバージョンの PostgreSQL で生成され、新バージョンのプランナーでは無視される可能性
  • 複数の相互に矛盾する助言が設定される場合

そのため、プラン助言を設定する際は、必ず本番環境での負荷テストを実施し、実際に期待通りのパフォーマンス向上を確認する必要があるという点を強調しておきたいです。

参考として、EnterpriseDB は同時期に「EDB Postgres AI」という自己最適化型のデータベースプラットフォームを発表していますが、このような AI による自動最適化とユーザー主導の pg_plan_advice は、今後の PostgreSQL エコシステムにおいて補完的な役割を担うと考えられます。