回答:
Explaining_EXPLAIN.pdfも役立ちます。
私がいつも混乱しているのは、初期コストと総コストです。私はそれを忘れるたびにこれをグーグルします、それは私をここに戻します、それは違いを説明しません、それが私がこの答えを書いている理由です。これは私がPostgresのEXPLAINドキュメントから収集したものであり、私が理解しているときに説明されています。
フォーラムを管理するアプリケーションの例を次に示します。
EXPLAIN SELECT * FROM post LIMIT 50;
Limit  (cost=0.00..3.39 rows=50 width=422)
  ->  Seq Scan on post  (cost=0.00..15629.12 rows=230412 width=422)
PgAdminからのグラフィカルな説明は次のとおりです。
(PgAdminを使用している場合は、コンポーネントにマウスを合わせると、コストの詳細が表示されます。)
コストはタプルとして表されます。たとえば、LIMITis cost=0.00..3.39のコストと順次スキャンのコストpostはcost=0.00..15629.12です。タプルの最初の数値は初期費用で、2番目の数値は総費用です。私は使用EXPLAINしていたのでEXPLAIN ANALYZE、これらのコストは実際の測定値ではなく推定値です。
厄介なことに、各「親」ノードのコストには、その子ノードのコストが含まれます。テキスト表現では、ツリーはインデントで表されLIMITますSeq Scan。たとえば、親ノードであり、その子です。PgAdmin表現では、矢印は子から親(データフローの方向)を指しています。これは、グラフ理論に精通している場合は直観に反する可能性があります。
ドキュメントには、コストにはすべての子ノードが含まれていると記載されていますが、親3.39の合計コストはその子の合計コストよりもはるかに小さいことに注意して15629.12ください。のようなコンポーネントLIMITは入力全体を処理する必要がないため、総コストは包括的ではありません。PostgresのドキュメントのEXPLAIN SELECT * FROM tenk1 WHERE unique1 < 100 AND unique2 > 9000 LIMIT 2;例を参照してください。EXPLAIN
上記の例では、どちらのコンポーネントも行の書き込みを開始する前に処理を行う必要がないため、両方のコンポーネントの起動時間はゼロです。シーケンシャルスキャンはテーブルの最初の行を読み取って出力します。LIMITその最初の行を読み取り、それを放出します。
コンポーネントが行の出力を開始する前に、多くの処理を行う必要があるのはいつですか?考えられる理由はたくさんありますが、1つの明確な例を見てみましょう。これは前と同じクエリですが、現在はORDER BY句が含まれています。
EXPLAIN SELECT * FROM post ORDER BY body LIMIT 50;
Limit  (cost=23283.24..23283.37 rows=50 width=422)
  ->  Sort  (cost=23283.24..23859.27 rows=230412 width=422)
        Sort Key: body
        ->  Seq Scan on post  (cost=0.00..15629.12 rows=230412 width=422)
そしてグラフィカルに:
繰り返しになりますが、順次スキャンpostの開始コストはありません。行の出力がすぐに開始されます。ただし、1行でも出力する前にテーブル全体23283.24をソートする必要があるため、ソートの開始にはかなりのコストがかかります。並べ替えの合計コストは、23859.27開始コストよりもわずかに高くなります。これは、データセット全体が並べ替えられると、並べ替えられたデータが非常に迅速に出力されるという事実を反映しています。
お知らせの起動時間は、というLIMIT 23283.24一種の起動時間に正確に等しいです。これはLIMIT、起動時間が長いためではありません。起動時間自体は実際にはゼロですがEXPLAIN、各親のすべての子コストをロールアップするため、LIMIT起動時間にはその子の合計起動時間が含まれます。
このコストのロールアップは、個々のコンポーネントの実行コストを理解することを困難にする可能性があります。たとえば、私たちのLIMIT起動時間はゼロですが、一見するとそれは明らかではありません。そのため、Hubert Lubaczewski(別名depesz)によって作成されたツールであるExplain.depesz.comにリンクしている他の人々もいます。これは、とりわけ、EXPLAIN親コストから子コストを差し引くことによって理解するのに役立ちます。彼は自分のツールに関する短いブログ投稿で他のいくつかの複雑さについて言及しています。
最もインデントされたものから最もインデントされていないものまで実行され、計画の下部から上部へと信じています。(したがって、インデントされたセクションが2つある場合、ページの下の方が最初に実行され、次に他のセクションが実行されると、それらを結合するルールが実行されます。
アイデアは、各ステップで1つまたは2つのデータセットが到着し、いくつかのルールによって処理されるというものです。データセットが1つだけの場合、その操作はそのデータセットに対して行われます。(たとえば、インデックスをスキャンして、必要な行を特定するか、データセットをフィルター処理するか、または並べ替えます。)2つの場合、2つのデータセットはさらにインデントされた2つのものであり、それらは表示されるルールによって結合されます。ほとんどのルールの意味はかなり簡単に推測できます(特に、以前に一連の説明プランを読んだことがある場合)。ただし、ドキュメントを調べるか、または(簡単に)フレーズを入力するだけで、個々の項目を確認できます。のようないくつかのキーワードと一緒にグーグルEXPLAIN。
これは明らかに完全な説明ではありませんが、通常は必要なことを何でも理解できる十分なコンテキストを提供します。たとえば、実際のデータベースからこの計画を検討してください:
explain analyze
select a.attributeid, a.attributevalue, b.productid
from orderitemattribute a, orderitem b
where a.orderid = b.orderid
and a.attributeid = 'display-album'
and b.productid = 'ModernBook';
------------------------------------------------------------------------------------------------------------------------------------------------------------
 Merge Join  (cost=125379.14..125775.12 rows=3311 width=29) (actual time=841.478..841.478 rows=0 loops=1)
   Merge Cond: (a.orderid = b.orderid)
   ->  Sort  (cost=109737.32..109881.89 rows=57828 width=23) (actual time=736.163..774.475 rows=16815 loops=1)
         Sort Key: a.orderid
         Sort Method:  quicksort  Memory: 1695kB
         ->  Bitmap Heap Scan on orderitemattribute a  (cost=1286.88..105163.27 rows=57828 width=23) (actual time=41.536..612.731 rows=16815 loops=1)
               Recheck Cond: ((attributeid)::text = 'display-album'::text)
               ->  Bitmap Index Scan on (cost=0.00..1272.43 rows=57828 width=0) (actual time=25.033..25.033 rows=16815 loops=1)
                     Index Cond: ((attributeid)::text = 'display-album'::text)
   ->  Sort  (cost=15641.81..15678.73 rows=14769 width=14) (actual time=14.471..16.898 rows=1109 loops=1)
         Sort Key: b.orderid
         Sort Method:  quicksort  Memory: 76kB
         ->  Bitmap Heap Scan on orderitem b  (cost=310.96..14619.03 rows=14769 width=14) (actual time=1.865..8.480 rows=1114 loops=1)
               Recheck Cond: ((productid)::text = 'ModernBook'::text)
               ->  Bitmap Index Scan on id_orderitem_productid  (cost=0.00..307.27 rows=14769 width=0) (actual time=1.431..1.431 rows=1114 loops=1)
                     Index Cond: ((productid)::text = 'ModernBook'::text)
 Total runtime: 842.134 ms
(17 rows)
自分で読んでみて、意味があるかどうかを確認してください。
私が読んだのは、データベースが最初にid_orderitem_productidインデックスをスキャンし、それを使用して必要な行を見つけorderitem、次にクイックソートを使用してそのデータセットをソートし(データがRAMに収まらない場合は使用されるソートが変更されます)、それを脇に置きます。
次に、スキャンしてorditematt_attributeid_idx目的の行を見つけ、orderitemattributeクイックソートを使用してそのデータセットをソートします。
次に、2つのデータセットを受け取り、それらをマージします。(マージ結合は、並べ替えられた2つのデータセットを並列にウォークし、一致したときに結合された行を出力する、一種の「圧縮」操作です。)
私が言ったように、あなたは計画の内側から外側へ、下から上へと作業します。
PostgreSQLの公式ドキュメントは、explainの出力を理解する方法について、興味深い完全な説明を提供しています。