発展関連トピック

実行計画(EXPLAIN)の読み方

定義

実行計画(EXPLAIN)とは、クエリオプティマイザが選択したデータアクセス方法・結合順・見積り件数などを示す情報であり、パフォーマンスチューニングの起点になる。

EXPLAIN で「オプティマイザの計画表」を見る

クエリの前に EXPLAIN を付けて実行すると、DBが選んだ実行計画をツリーで返してきます。 「どのテーブルにどうアクセスして、どの順で結合して、どうソートするか」がここに書かれていて、パフォーマンスチューニングの読み解きの起点になります。

EXPLAIN
SELECT * FROM orders
WHERE customer_id = 42;

出力(例):

 Index Scan using idx_orders_customer_id on orders
   Index Cond: (customer_id = 42)

idx_orders_customer_id を使ってインデックス経由でアクセスしたcustomer_id = 42 という条件をインデックスにかけた──ということが読み取れます。もしインデックスが無ければ次のような出力になります:

 Seq Scan on orders
   Filter: (customer_id = 42)

こちらは全表スキャンで、テーブルの全行を舐めながらcustomer_id = 42Filter として適用しています。件数が多いと重くなります。

主なアクセス方法

Seq Scan(全表スキャン)

テーブルを先頭から順に読む。インデックスがない、使えない条件(LIKE '%foo'や関数変換など)、あるいは対象行が多すぎてインデックスの方が遅い場合に選ばれます。 RDBMSによっては「Full Table Scan」と呼ばれます。

Index Scan

B-tree を辿って該当行の行ID を取得 → その行を含むページを読む方式。 出力に Index Cond: が見えたら「インデックスが実際に使われている」証拠です。

Index Only Scan

カバリングインデックスで、必要なカラムがインデックス側にすべて含まれていて、テーブル本体を1回も読まずに済むケース。最速のパターン。 RDBMSによっては「Using index」と表現されます。

Nested Loop / Hash Join / Merge Join

JOINの3つの代表的なアルゴリズム。オプティマイザが両側のテーブルサイズと使えるインデックスから最適なものを選びます。

  • Nested Loop: 外側を1件ずつ、内側で対応する行を探す。内側にインデックスがあると速い
  • Hash Join: 一方でハッシュテーブルを作り、他方から突き合わせる。大量データ同士に向く
  • Merge Join: 両側がキー順にソート済みなら効率的にマージできる

読む順番はツリーの内側から

実行計画はインデントが深いところ(葉)から根に向かって実行されます。

 Nested Loop
   ->  Index Scan using idx_users_email      -- 先にこれ
         Index Cond: email = 'a@example.com'
   ->  Index Scan using idx_orders_customer_id  -- 次にこれ
         Index Cond: customer_id = u.id

まず外側 (users) を email インデックスで絞り込み、その結果1件ずつについて内側 (orders) のcustomer_id インデックスを引いてマッチする行を得る、という流れ。 末端のノードから読むと理解しやすいです。

どこを見ればいい?(チェックリスト)

  1. 大きなテーブルに Seq Scan が出ていないか → インデックス化の候補
  2. Nested Loopの内側が Seq Scan になっていないか → 内側にインデックスがない典型的な低速パターン
  3. 推定 rows と実測 rows の乖離 統計情報 が古い可能性
  4. 意外な JOIN アルゴリズムが選ばれていないか → 統計情報のずれで最適でない選択になっているかも

EXPLAIN と EXPLAIN ANALYZE

EXPLAIN はプランを見積りだけ返します(クエリは実行しません)。 一方 EXPLAIN ANALYZE実際にクエリを実行して、実測時間と実測行数も返します。 「推定と実測の乖離」を見るには ANALYZE 必須。

ただし ANALYZE は本当にクエリを実行するので、INSERT/UPDATE/DELETE 系で試すと本当にデータが変わります。SELECT で使うのが安全です。

よくある疑問

Q.EXPLAINとEXPLAIN ANALYZEの違いは?
A.EXPLAINは見積り(コスト・行数)だけを返しますが、EXPLAIN ANALYZEは実際にクエリを実行して実測時間・実測行数も表示します。本番でANALYZEを実行するとデータの更新も走るので注意(SELECTなら基本的に問題なし)。
Q.計画の中で見るべきポイントは?
A.アクセス方法(Seq/Index/Hash)、コスト、推定行数と実行数の乖離、結合方法、行の増減が起きる場所。特に「推定と実測の乖離が大きい」場所は統計情報が古い or 相関を捉えられていない可能性が高い。
Q.全表スキャンが出たら必ずインデックスを貼るべき?
A.件数が少ないテーブルや、対象行の割合が高いクエリでは全表スキャンの方が速いこともあります。EXPLAINは判断材料であって、コストと行数を見て総合判断します。

関連トピック

もっと学びたい方へ(おすすめ書籍)

本セクションはAmazonアソシエイトのリンクを含みます。購入いただくと運営者に紹介料が入る場合があります。

オンライン個別指導

もっと深くDBを学びたい方へ。

たいてっくが、SQL・データベース設計・パフォーマンスチューニング・ IPAデータベーススペシャリスト対策まで、1対1で学習をサポートします。まずは無料相談から。

無料相談を予約する →