EXPLAIN ANALYZE:クエリの「嘘」を見抜くための唯一の武器
データベースエンジニアとして長く現場にいると、若手からこんな相談をよく受ける。「インデックスを貼ったはずなのに、なぜかクエリが遅いんです」。
その時、私は決まってこう返す。「EXPLAIN ANALYZE を取ったか?」と。
単なる `EXPLAIN` は、オプティマイザが見ている「夢」に過ぎない。統計情報に基づいて導き出された、いわば机上の空論だ。一方で `EXPLAIN ANALYZE` は、冷徹な現実を突きつけてくる。実際にクエリを実行し、何が起き、どこで詰まり、どの程度のコストがかかったのか。その「答え合わせ」こそが、パフォーマンスチューニングのスタート地点だ。
今回は、単なるコマンドの使い方を超えて、この強力なツールをどう読み解き、PostgreSQLの深淵を覗き込むかについて、少し掘り下げて話そうと思う。
—
「推定」と「実測」の乖離に潜むもの
`EXPLAIN ANALYZE` の出力を見ると、 `cost=…`(推定コスト)と `actual time=…`(実測時間)が並んでいるのがわかるだろう。ここが大きく乖離しているとき、我々エンジニアは「何が起きているのか?」を直感的に探らなければならない。
よくあるのが、統計情報の鮮度不足だ。
PostgreSQLのオプティマイザは、テーブルの分布状況を統計情報(`pg_statistic`)から推測する。例えば、特定のカラムに偏りがあるデータや、相関関係があるカラムを複数使った検索など、オプティマイザが「単純な独立事象」と仮定して計算を誤るケースは少なくない。
- loops の値に注目せよ:特にネステッドループ結合において、想定よりも `loops` が回っているなら、それは内側のテーブルに対するインデックスの効きが悪いか、駆動表(Outer table)の行数見積もりが外れている証拠だ。
実行計画の「ノード」を読み解く深層心理
実行計画の出力にある各ノード(`Index Scan`, `Seq Scan`, `Hash Join` など)をただ眺めるのではなく、「なぜオプティマイザはこの道を選んだのか」という設計思想まで読み解く必要がある。
例えば、Hash Join vs Merge Join。
データ量が少ないうちは Hash Join が高速だが、メモリ(`work_mem`)が不足してディスクへのスピル(一時ファイルへの書き出し)が発生した瞬間、パフォーマンスは劇的に悪化する。`EXPLAIN ANALYZE` の出力に `Batches: 1` とあればメモリ内に収まっているが、これが `Batches: 10` とかになっていたら、即座に `work_mem` のチューニングを検討すべきだ。
また、`Filter` と `Index Cond` の違いも重要だ。
- `Index Cond`:インデックスを使って効率的に範囲を絞り込めている。
- `Filter`:インデックスで絞り込んだ後、さらに全件に近いデータをメモリ上で舐めて条件判定している。
もし `Filter` に膨大な行数がかかっているなら、それは「複合インデックスの設計ミス」か「インデックスの順序が逆」である可能性が高い。
パフォーマンスチューニングの極意:静寂を待つ
最近、私は `EXPLAIN (ANALYZE, BUFFERS)` を常用している。単なる実行時間だけでなく、`Shared Hit` や `Read` といったバッファ情報を確認するためだ。
`Shared Hit` が異常に多い場合、インデックスは効いているが「読み込みすぎていないか?」を疑う。逆に `Read` が多い場合は、キャッシュ効率が悪いか、ワーキングセットが物理メモリに収まっていないことを示唆している。
一つ、現場の知恵を共有しよう。
本番環境で `EXPLAIN ANALYZE` を実行するのは、ある種の「外科手術」だ。書き込み系のクエリ(`UPDATE` や `DELETE`)に対して実行すると、当然ながらデータが更新されてしまう。そんなときは、トランザクションの中で実行して最後にロールバックするか、あるいは `EXPLAIN (ANALYZE, TIMING OFF)` を使ってオーバーヘッドを抑える工夫も必要になる。
—
最後に:道具に支配されるな
`EXPLAIN ANALYZE` は素晴らしいツールだが、あくまで「ツール」だ。
「計画が遅い=インデックスを貼ればいい」という短絡的な思考は、時にインデックスのオーバーヘッドを増大させ、バキュームの負荷を上げ、システム全体の首を絞めることになる。
真のエンジニアは、実行計画から「データベースがこのクエリをどう処理したいと願っているか」を読み取り、その意図に沿ったデータ構造を設計する。
クエリと対話し、統計情報と仲良くなり、そして冷徹な数字から真実を導き出す。
これこそが、PostgreSQLを操る面白さではないだろうか。
皆さんのクエリが、今日も効率的に、そして美しく実行されることを願っている。
コメント