EXPLAINプランの読み方
クエリプランにおけるスキャン方式、結合方式、コスト見積もりを読み解きます。
「EXPLAINプランの読み方」はCoddyKit上の無料Coding Interview Prepレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはCoding Interview Prep学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Coding Interview Prepコースには全4レッスンが含まれています。
面接官が EXPLAIN について尋ねる理由
シニア向けの面接段階になると、面接官は「クエリを書いてください」ではなく、「このクエリはなぜ遅いのでしょうか」と尋ねるようになります。その問いに答えるツールが EXPLAIN です。
EXPLAIN はデータベースの実行計画を表示します。これは、プランナーがSQLを実行するために選んだ手順ごとの戦略です。どのテーブルがスキャンされるか、どの順序で結合されるか、各ステップにどの程度のコストがかかるかを概算で確認できます。
実行計画を読めることは、構文だけでなくエンジンの仕組みを理解していることの証明になります。まさにこれが、面接官が中級レベルとシニアレベルを見分けるポイントです。
EXPLAIN と EXPLAIN ANALYZE
2つの種類があり、面接官はこの違いを好んで尋ねます。
- EXPLAIN は、クエリを実行せずにプランナーの推定実行計画を表示します。高速で安全です。
- EXPLAIN ANALYZE はクエリを実際に実行し、推定値とともに実際の行数と実行時間を報告します。
特に重要なのは、推定行数と実際の行数を比較することです。大きな不一致がある場合、プランナーが不適切な統計情報を使っており、誤った選択をしている可能性があります。
注意点として、EXPLAIN ANALYZE はクエリを実際に実行します。そのため、ロールバックされるトランザクションで包まない限り、INSERT や UPDATE も実行されます。
EXPLAIN ANALYZE
SELECT * FROM orders WHERE customer_id = 42;ツリーの読み方
実行計画は一覧ではなくツリーです。最も深くインデントされたノードが最初に実行される葉であり、結果は上へ流れて、最終出力を生成するルートに到達します。
内側から外側へ読みます。最も深いノードを見つけてください。そこから実行が始まります。各親ノードは、子ノードが出力した行を受け取ります。
面接では、次のように説明します。「まずこのテーブルをスキャンし、その行をこの結合に渡します。結合の結果をソートに渡し、ソート結果をLIMITに渡します。」この下から上へ進む説明が、面接官の聞きたい内容です。
プランノードの構造
Postgresのすべてのノードには、同じ主要な数値が含まれます。
- cost=0.00..35.50 任意のプランナー単位で表したスタートアップコスト..合計コスト
- rows=1000 生成される行数の推定値
- width=64 行サイズの推定平均値(バイト)
最初のコストはスタートアップコストです(ハッシュテーブルの構築など、最初の行が現れるまでに必要な処理のコストです)。2つ目は、すべての行を返すための合計コストです。合計コストが高いほど、プランナーは相対的に処理が重いと見積もっています。
Seq Scan on orders (cost=0.00..35.50 rows=1000 width=64)具体例
単純なフィルタ付きクエリを考えてみましょう。以下のプランは、1行で処理内容を示しています。
これはordersに対するSeq Scan(テーブル全体の読み取り)で、status = 'shipped'というフィルターを適用しています。プランナーは、一致する行を1000行と見積もっています。
ordersに1000万行あり、そのうち一致するのが1000行だけなら、面接では次のように答えることが期待されます。ここでのシーケンシャルスキャンは非効率です。status(または、より選択性の高い列)にインデックスを作成すれば、テーブル全体を読み取らずに済みます。
EXPLAIN SELECT * FROM orders WHERE status = 'shipped';
Seq Scan on orders (cost=0.00..18334.00 rows=1000 width=64)
Filter: (status = 'shipped'::text)推定行数と実際の行数
EXPLAIN ANALYZEを使うと、括弧内に実際の数値も表示されます。
例を見てみましょう。プランナーは行数を1000行と推定しましたが、実際には480000行が取得されました。これは480倍の過小見積もりです。プランナーは少ない行数を前提に戦略を選択したため、実際のデータに対しては、その選択が間違っている可能性が高いといえます。
面接では、この差を診断の要点として示します。統計情報が古くなっています。テーブルに対してANALYZEを実行すれば、プランナーはより適切なプランを選ぶ可能性が高くなります。
Seq Scan on orders
(cost=0.00..18334.00 rows=1000 width=64)
(actual time=0.02..210.4 rows=480000 loops=1)loops=Nの意味
loopsの値は、候補者が考える以上に重要です。これは、ノードが実行された回数です。
この値は、ネステッドループ結合の内側にあるノードに表示されます。内側のノードは外側の各行につき1回実行されます。loops=480000なら、その内側の処理は48万回実行されたことになります。
重要なのは、表示される1行あたりの時間と行数が1ループあたりの値だという点です。実際の合計値を求めるには、loopsを掛けます。1ループあたり0.004msで安価に見えるノードでも、480000ループでは約2秒になります。
Index Scan using idx_cust on orders
(actual time=0.003..0.004 rows=1 loops=480000)コストは相対値であり、ミリ秒ではありません
よくある落とし穴があります。候補者がcost=18334を見て、18秒かかりますと言うことがあります。これは誤りです。
コストは任意のプランナー単位で表され、シーケンシャルなページ読み取り1回が1.0になるよう調整されています。これはプラン同士を比較する場合にのみ意味があり、実時間を表す値ではありません。
実際の処理時間を知るには、EXPLAIN ANALYZEと、ミリ秒単位で計測されたactual timeの値が必要です。面接ではこの点を明確に説明してください。メトリクスを本当に理解していることを示せます。
結合プランの読み方
ここに2つのテーブルを使ったプランがあります。下から上へ読みます。
まず2つのスキャンで、ordersとcustomersから行を取得します。それらをHash Joinに渡します。一方の側でハッシュを作成し、もう一方の側でそのハッシュを検索します。結合の出力は、続いて最終結果に渡されます。
インデントが構造を示していることに注目してください。両方のスキャンがHash Joinの下にあります。面接官は、結合方式(ここではハッシュ結合)と、どのテーブルがハッシュ化されているか(通常は小さい方)を特定できることを求めています。
Hash Join (cost=30.0..520.0 rows=900 width=72)
Hash Cond: (o.customer_id = c.id)
-> Seq Scan on orders o (cost=0..400 rows=10000)
-> Hash (cost=18..18 rows=500)
-> Seq Scan on customers c (cost=0..18 rows=500)指摘すべき危険信号
どのプランでも、次の警告サインを見抜けるように練習しましょう。
- 選択性の高いフィルターがある巨大なテーブルに対するSeq Scan。インデックスが役立つ可能性があります。
- 推定行数と実際の行数が大きく異なる。統計情報が古くなっています。
- 大きなテーブルに対する、ループ回数の多いNested Loop。多くの場合、内側の結合キーにインデックスがありません。
- SortまたはHashがディスクにあふれている(
Diskの使用量として表示されます)。work_memが小さすぎます。 - Rows Removed by Filterの値が非常に大きい。テーブルの大部分を読み取って破棄しています。
出力形式とBUFFERS
プランにはいくつかの形式があります。デフォルトのTEXTは、面接で読み上げる内容です。ただし、構造化された出力を要求することもできます。
EXPLAIN (FORMAT JSON)またはFORMAT YAMLを指定すると、ツールやダッシュボードで解析できる機械可読形式のプランが生成されます。手作業で扱う機会はほとんどありませんが、これらの形式があることを知っていると、シニアらしさを示せます。
オプションは括弧内に追加します。EXPLAIN (ANALYZE, BUFFERS)のように指定します。BUFFERSオプションは、キャッシュヒットとディスク読み取りを報告します。I/Oがボトルネックになっているクエリの診断に非常に役立ちます。
EXPLAIN (ANALYZE, BUFFERS)
SELECT * FROM orders WHERE customer_id = 42;確認問題
面接官から、コストのセクションにrows=1000があり、actual ... rows=480000も表示されているEXPLAIN ANALYZEのノードを見せられました。最も可能性の高い診断は何でしょうか。
まとめ
これで、シニアエンジニアのようにプランを読めるようになりました。
EXPLAINは推定を行い、EXPLAIN ANALYZEは実行して計測します。- ツリーは下から上へ読みます。葉ノードが先に実行され、ルートノードが出力を生成します。
- 各ノードにはコスト(相対単位)、行数、幅が表示されます。
actual timeが実際のミリ秒単位の値です。 loopsは1ループあたりの値に掛け合わせられます。ネステッドループには注意してください。- 推定行数と実際の行数の差が、最も重要な診断シグナルです。
プランを声に出して説明し、危険信号を指摘することが、面接で評価される行動です。
よくある質問
「EXPLAINプランの読み方」レッスンは無料ですか?
はい。「EXPLAINプランの読み方」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Coding Interview Prepコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Coding Interview Prepコースには全4レッスンが含まれています。
「EXPLAINプランの読み方」で何を学びますか?
クエリプランにおけるスキャン方式、結合方式、コスト見積もりを読み解きます。 ブラウザで直接実行するハンズオンコードでCoding Interview Prepを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Coding Interview Prepを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのCoding Interview Prepは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。
「EXPLAINプランの読み方」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このCoding Interview Prepレッスンでコードを書いて実行できますか?
はい。すべてのCoding Interview Prepレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。