【実務・中級編】 クエリプランナ – PostgreSQL

なぜ君のクエリは遅いのか?PostgreSQLの「頭脳」、クエリプランナと仲良くなる話

現場でコードを書いていると、たまに「なんでこんな単純なクエリがこんなに遅いんだ?」と頭を抱える夜があるよね。

インデックスは貼った、テーブル定義も悪くない。それなのに、PostgreSQLが選んだ実行計画(Explain Plan)を見ると、なぜかフルスキャン(Seq Scan)を連発している……。

実は、PostgreSQLのパフォーマンスチューニングにおいて最も重要なのは、「クエリプランナ」という名の優秀だけど時々頑固な執事と対話することなんだ。今日は、このプランナが裏側で何を考えているのか、どうすれば彼らの機嫌を損ねずにパフォーマンスを引き出せるのか、少しだけ深掘りしてみよう。

—

1. クエリプランナは「確率論のギャンブラー」だ

まず誤解を解いておこう。クエリプランナは、常に「最短ルート」を見つけているわけじゃない。彼がやっているのは、「統計情報を元にした、コスト計算に基づく最適化」だ。

SQLが投げられると、プランナは以下のステップを踏む。

1. パーサ・アナライザ: SQLの構文が合っているかチェックし、オブジェクト(テーブルやカラム)が実在するか確認する。
2. リライタ: ビューの展開や、ルールベースの書き換えを行う。
3. プランナ(ここが心臓部!):

  • 統計情報(`pg_statistic`)を参照。
  • 複数の実行戦略(Nested Loopなのか、Hash Joinなのか等)を検討。
  • それぞれに「コスト」を割り振る。
  • 最もコストが低いルートを採用する。

ここで肝心なのは、プランナが「統計情報」を信じ切っているということ。もし、君が`ANALYZE`をサボって統計情報が古くなっていたら? プランナは「このテーブル、件数少ないから全部メモリに載せちゃえ」という致命的な判断ミスを犯す。これが「遅いクエリ」の定石だね。

—

2. 実行計画を「読む」という技術

現場でトラブルが起きたとき、迷わず `EXPLAIN (ANALYZE, BUFFERS)` を叩く癖はついているかな?

EXPLAIN (ANALYZE, BUFFERS)
SELECT FROM orders WHERE user_id = 12345;

出力結果で特に注目してほしいのは、以下の3点だ。

  • Actual Cost: プランナが見積もったコストと、実際の実行時間の乖離。ここが大きくズレていたら、統計情報が死んでいる証拠だ。
  • Buffers: `shared hit`(メモリヒット)と `read`(ディスク読み込み)の数。ここが `read` だらけなら、メモリ不足かインデックスが効いていない。
  • Rows Removed by Filter: WHERE句の条件で、せっかく読み込んだのに行を捨てていないか? それはインデックスの貼り方が甘いサインだ。

—

3. プランナを「誘導」する技術的アプローチ

たまに、どれだけ統計情報を更新しても、プランナが「あえて遅い道」を選ぶことがある。そんな時は、僕たちエンジニアが少しだけ誘導してやる必要がある。

① 統計情報の鮮度を保つ(基本中の基本)

`VACUUM ANALYZE` を定期的に実行するのは当然として、特定のカラムの分布が偏っている場合は `ALTER TABLE … ALTER COLUMN … SET STATISTICS` で統計情報の精度を上げることもできる。プランナの解像度を上げるイメージだね。

② ヒント句を使わず「SQLの書き方」で制御する

PostgreSQLにはMySQLのような「インデックスヒント」がない(それがPostgreSQLの流儀だ)。だからこそ、クエリの書き方を工夫する。

例えば、複雑なJOINでプランナが迷走している場合、一時テーブルに切り出すか、あるいは `CTE (Common Table Expressions)` を使って「まずはここで結果を絞り込む」という意図を明確にしてやるんだ。

— プランナが混乱しそうな複雑なJOIN
WITH filtered_orders AS (
SELECT FROM orders WHERE created_at > ‘2023-01-01’
)
SELECT FROM filtered_orders f
JOIN users u ON f.user_id = u.id;

※ PostgreSQL 12以降はCTEが最適化されやすくなったけれど、それでも「処理の順序」を論理的に明示することで、プランナの選択肢を絞り込み、迷子を防ぐことができる。

—

最後に:プランナは「敵」じゃない

たまに「プランナが馬鹿な選択をした」と怒るエンジニアがいるけれど、プランナは常にその瞬間の「手持ちの情報」だけでベストを尽くしているんだ。

もしプランナが期待通りに動かないなら、それは「渡している情報(統計情報やインデックスの設計)」が足りていないか、あるいは「クエリが複雑すぎて、プランナが探索できる範囲を超えている」かのどちらか。

まずは彼(プランナ)の視点に立って、 `EXPLAIN` の行間を読んでみてほしい。そうすれば、今までただの「実行結果」だったものが、PostgreSQLからの「ここを最適化してくれ!」というメッセージに見えてくるはずだよ。

さあ、今日はどのクエリを最適化しに行こうか? また現場で会おう。

コメント

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