【テクニカル・上級編】 クエリプランナ – PostgreSQL

PostgreSQLのクエリプランナ:SQLという「問い」が「物理的な手順」に変わる瞬間

PostgreSQLを長く触っていると、ふとした瞬間に「なぜPostgreSQLはこんな実行計画を選んだのか?」と頭を抱えたくなる夜があるはずです。インデックスは貼ってあるのにシーケンシャルスキャンが選ばれたり、あるいはNested Loopが暴走してシステムが重くなったり。

PostgreSQLのクエリプランナは、数あるデータベースエンジンの中でも特に「論理的な美しさ」と「複雑な現実」の狭間で戦っている、非常に面白いコンポーネントです。今日は、このプランナの深淵を少しだけ覗いてみましょう。

—

1. プランナは「魔法」ではなく「数学」を解いている

多くの人はプランナを「SQLを最適化してくれる魔法の箱」だと思いがちですが、中身はもっとドライです。プランナの役割は、与えられたSQLという「結果」に対して、考えうる膨大な数の「実行手順(Plan Tree)」を生成し、その中で最もコストの低いものを選ぶこと。

ここで重要なのは、プランナが「最速」を保証しているわけではなく、「コスト計算に基づいた最小リスク」を選んでいるに過ぎないということです。

  • Parser/Analyzer: SQLを構文解析し、Query Treeに変換する。
  • Rewriter: ビューやルールをQuery Treeに適用し、書き換える。
  • Planner/Optimizer: ここが本丸。Query TreeからPathを生成し、コスト比較を経てPlan Treeを生成する。

この中で特に面白いのが、PostgreSQLのコストモデルです。`seq_page_cost`(シーケンシャルアクセス単価)や`random_page_cost`(ランダムアクセス単価)といったパラメータが、実は「今のハードウェア環境」に適合しているかどうかが、すべての運命を握っています。SSDが当たり前の現代で、デフォルトのコスト設定のまま運用するのは、古い地図で迷路を歩くようなものかもしれません。

—

2. 「なぜ期待通りの計画にならないのか?」――深淵を覗く

現場で遭遇するパフォーマンストラブルの多くは、実はプランナの「勘違い」から始まります。原因のほとんどは統計情報、それも「相関関係」の欠如にあります。

プランナは、列ごとのカーディナリティ(値の多様性)は把握していますが、列間の依存関係までは推論できません。例えば、「都道府県」と「市区町村」という列がある場合、これらは完全に相関していますが、プランナは独立していると仮定して計算してしまいます。その結果、見積もりの積が極端に小さくなり、Nested Loopのような「小規模クエリには最適だが、大規模クエリでは破滅的」なプランが選ばれてしまうのです。

トラブルシューティングの定石:

1. `EXPLAIN (ANALYZE, BUFFERS)` を信じるな: 実行計画の `Actual Time` と `Estimated Time` を徹底的に比較してください。乖離が大きければ、統計情報が古いか、プランナがデータの偏りを見誤っています。
2. `CREATE STATISTICS` の検討: PostgreSQL 10以降で導入されたこの機能は、まさに前述の「列間の相関」をプランナに教えるための切り札です。マルチカラム統計を正しく定義するだけで、プランナの精度が劇的に改善することは珍しくありません。

—

3. 「物理実行計画」を操るプロの矜持

プランナを信頼しすぎるのも禁物ですが、かといって無理に `pg_hint_plan` や `SET enable_seqscan = off` でねじ伏せるのも、長期的な保守性を考えると悪手です。

僕が推奨するのは、プランナの思考を誘導することです。

  • クエリの構造を変える: 例えば、複雑なサブクエリをCTE(Common Table Expressions)に切り出す際、PostgreSQL 12以前ではそれが「最適化の境界」になり、プランナの視野を狭めていました。現代のバージョンでは `MATERIALIZED` / `NOT MATERIALIZED` の制御を意識することで、プランナにヒントを与えることができます。
  • インデックスの工夫: 単純なインデックスではなく、式インデックス(Expression Indexes)や部分インデックス(Partial Indexes)を使って、プランナが「これを使わざるを得ない」という状況を物理的に作り出すのも、熟練エンジニアのテクニックです。

—

最後に:プランナと対話する

PostgreSQLのクエリプランナは、常に進化しています。JITコンパイルの導入や、並列クエリの最適化など、エンジン自体は日々賢くなっています。

しかし、どんなに賢くなっても、そのSQLを書いた人間以上に「そのデータの意味」を知っている者はいません。プランナと仲良くするコツは、彼を盲信するのではなく、彼が何を考え、どこで迷っているのかを常に覗き見ること。

`EXPLAIN` の出力は、単なるテキストではありません。それは、データベースエンジンがあなたに送ってきた「思考の断片」なのです。次にクエリが遅いと感じたときは、ぜひその思考のプロセスをじっくりと眺めてみてください。きっと、解決の糸口はその深い森の中に隠されているはずです。

さて、今日はどのクエリを最適化しましょうか?

コメント

タイトルとURLをコピーしました