クエリプランの作成と解釈
クエリプランを使用して、クエリを実行するために必要な個々のオペレーションに関する情報を取得することができます。クエリプランを操作する前に、まず Amazon Redshift がクエリの処理とクエリプランの作成を扱う方法を理解することをお勧めします。詳細については、「クエリプランと実行ワークフロー」を参照してください。
クエリプランを作成するには、EXPLAINコマンドに続いて実際のクエリテキストを指定します。クエリプランから、次の情報が提供されます。
-
実行エンジンが実行する操作 (下から順に結果を読み取る)。
-
各操作で実行されるステップの種類。
-
各操作で使用されるテーブルと列。
-
各操作で処理されるデータの量 (行数とバイト単位のデータの幅)
-
操作の相対的なコスト。コストは、プラン内のステップの相対的な実行回数を比較する評価基準です。コストは、実際の実行回数やメモリの消費に関する正確な情報を提供したり、実行プラン間の意味のある比較を提供したりするわけではありません。ほとんどのリソースを消費しているクエリ内の操作を示しています。
EXPLAIN コマンドは実際にクエリを実行しません。クエリが現在の操作条件の下で実行される場合に Amazon Redshift が実行するプランが表示されるだけです。テーブルのスキーマまたはデータを変更し、ANALYZEをもう一度実行して統計メタデータを更新した場合、クエリプランが異なる可能性があります。
EXPLAIN によるクエリプラン出力は簡略化され、クエリ実行の概要になっています。並行クエリ処理の詳細を示すわけではありません。詳細な情報を表示するには、クエリ自体を実行した後、SVL_QUERY_SUMMARY ビューまたは SVL_QUERY_REPORT ビューからクエリの概要情報を取得します。これらのビューの使用の詳細については、「クエリの概要の分析」を参照してください。
次の例に、EVENT テーブルでの簡単な GROUP BY クエリの EXPLAIN 出力を示します。
explain select eventname, count(*) from event group by eventname; QUERY PLAN ------------------------------------------------------------------- XN HashAggregate (cost=131.97..133.41 rows=576 width=17) -> XN Seq Scan on event (cost=0.00..87.98 rows=8798 width=17)
EXPLAIN は、操作ごとに次のメトリクスを返します。
- Cost
-
プラン内で操作を比較する際に役立つ相対的な値。コストは、2 つのピリオドで区切られた 2 つの小数点値で構成されます (
cost=131.97..133.41
など)。最初の値 (この場合は 131.97) は、この操作の最初の行を返す相対コストを示します。2 番目の値 (この場合は 133.41) は、操作を完了する相対コストを示します。クエリプランのコストはプランを調べる際に累積されるので、この例の HashAggregate コスト (131.97..133.41) には、その下の Seq Scan コスト (0.00..87.98) が含まれます。 - Rows
-
返される行数の推定値。この例では、スキャンにより 8798 行が返されることが予期されます。HashAggregate 演算子自体は、576 行 (重複したイベント名より後は結果セットから破棄されます) を返すことが予期されます。
注記
行の予測は、ANALYZE コマンドによって生成された、利用可能な統計情報に基づいています。ANALYZE が最近実行されていない場合、予測の信頼性は下がります。
- Width
-
バイト単位の、予測される平均的な行の幅。この例では、平均的な行の幅は 17 バイトであると予期されます。
EXPLAIN の演算子
このセクションでは、EXPLAIN 出力で最もよく使用される演算子を簡単に示します。演算子の詳細なリストについては、SQL コマンドセクションの「EXPLAIN」を参照してください。
Sequential Scan 演算子
Sequential Scan 演算子 (Seq Scan) は、テーブルスキャンを示します。Seq Scan はテーブル内の各列を最初から最後まで連続的にスキャンし、各行のクエリ制約を (WHERE 句で) 評価します。
Join 演算子
Amazon Redshift は、結合されるテーブルの物理的な設計、結合に必要なデータの場所、クエリ固有の要件に基づいて、結合演算子を選択します。
-
Nested Loop
最適性が最も低いネステッドループは、主にクロス結合 (デカルト積) および一部の不等結合に使用されます。
-
Hash Join および Hash
ハッシュ結合およびハッシュは、通常はネステッドループ結合よりも高速で、内部結合および左右の外部結合に使用されます。これらの演算子は、結合列が分散キーでもソートキーでもないテーブルを結合するときに使用されます。ハッシュ演算子は結合の内部テーブルのハッシュテーブルを作成します。ハッシュ結合演算子は外部テーブルを読み取り、結合列をハッシュし、内部ハッシュテーブルで一致を検索します。
-
Merge Join
通常、結合が高速であれば、内部結合と外部結合にマージ結合が使用されます。マージ結合は完全結合には使用されません。この演算子は、結合列が分散キーとソートキーの両方である結合テーブルで、結合テーブルの 20 % 未満がソートされていない場合に使用されます。また、ソートされた 2 つのテーブルを順に読み取り、一致する行を検索します。ソートされていない列の割合を表示するには、SVV_TABLE_INFOシステムテーブルをクエリします。
-
空間結合
通常、空間データの近接性に基づく高速結合であり、
GEOMETRY
とGEOGRAPHY
のデータ型に使用されます。
Aggregate 演算子
クエリプランは、集計関数および GROUP BY 操作を含むクエリで次の演算子を使用します。
-
Aggregate
AVG や SUM などのスカラー集計関数の演算子。
-
HashAggregate
未ソートのグループ化された集計関数の演算子。
-
GroupAggregate
ソート済みのグループ化された集計関数の演算子。
ソート演算子
クエリプランは、クエリで結果セットをソートまたは結合する必要があるときに次の演算子を使用します。
-
並べ替え
ORDER BY 句およびその他のソート操作 (UNION クエリや結合、SELECT DISTINCT クエリ、ウィンドウ関数で必要となるソートなど) を評価します。
-
Merge
並行操作から導出される、ソートされた中間結果に従って最終的なソート結果を作成します。
UNION、INTERSECT、および EXCEPT 演算子
クエリプランは、UNION, INTERSECT、および EXCEPT を使用したセット操作を含むクエリに次の演算子を使用します。
-
サブクエリ
UNION クエリを実行するのに使用されます。
-
Hash Intersect [Distinct]
INTERSECT クエリを実行するために使用されます。
-
SetOp Except
EXCEPT (または MINUS) クエリの実行に使用されます。
その他の演算子
次の演算子は、ルーチンクエリの EXPLAIN 出力にも頻繁に出現します。
-
Unique
SELECT DISTINCT クエリと UNION クエリの重複を削除します。
-
制限
LIMIT 句を処理します。
-
Window
ウィンドウ関数を実行します。
-
結果
テーブルアクセスを伴わないスカラー関数を実行します。
-
Subplan
特定のサブクエリに使用されます。
-
ネットワーク
さらに処理するために中間結果をリーダーノードに送ります。
-
Materialize
ネストループ結合および一部のマージ結合への入力のために行を保存します。
EXPLAIN での結合
クエリオプティマイザは、クエリと基礎となるテーブルの構造に応じて、異なる結合の種類を使用してテーブルデータを取得します。EXPLAIN 出力は、結合の種類、使用するテーブル、およびクラスター全体にテーブルデータが分散される方法を参照して、クエリの処理方法を説明します。
結合の種類の例
次の例は、クエリオプティマイザが使用できるさまざまな結合の種類を示しています。クエリプランに使用される結合の種類は、関係するテーブルの物理的な設計によって異なります。
例: 2 つのテーブルのハッシュ結合
次のクエリは、CATID 列で EVENT と CATEGORY を結合します。CATID は CATEGORY の分散キーおよびソートキーですが、EVENT の分散キーおよびソートキーではありません。ハッシュ結合は、EVENT を外部テーブルとして、CATEGORY を内部テーブルとして実行されます。CATEGORY は小さいテーブルであるため、プランナーは DS_BCAST_INNER を使用してクエリ処理中にそのコピーをコンピューティングノードにブロードキャストします。この例の結合コストは、プランの累積コストのほとんどを占めます。
explain select * from category, event where category.catid=event.catid; QUERY PLAN ------------------------------------------------------------------------- XN Hash Join DS_BCAST_INNER (cost=0.14..6600286.07 rows=8798 width=84) Hash Cond: ("outer".catid = "inner".catid) -> XN Seq Scan on event (cost=0.00..87.98 rows=8798 width=35) -> XN Hash (cost=0.11..0.11 rows=11 width=49) -> XN Seq Scan on category (cost=0.00..0.11 rows=11 width=49)
注記
EXPLAIN 出力の演算子が揃ってインデントされていることは、それらの操作が相互に依存せず、並行して開始できることを示している場合があります。前の例では、EVENT テーブルおよびハッシュ操作のスキャンは揃っていませんが、EVENT スキャンはハッシュ操作が完全に完了するまで待機する必要があります。
例: 2 つのテーブルのマージ結合
次のクエリでも SELECT * が使用されますが、LISTID が両方のテーブルの分散キーおよびソートキーとして設定されている LISTID 列の SALES と LISTING が結合されます。マージ結合が選択され、結合にデータの再分散は必要ありません (DS_DIST_NONE)。
explain select * from sales, listing where sales.listid = listing.listid; QUERY PLAN ----------------------------------------------------------------------------- XN Merge Join DS_DIST_NONE (cost=0.00..6285.93 rows=172456 width=97) Merge Cond: ("outer".listid = "inner".listid) -> XN Seq Scan on listing (cost=0.00..1924.97 rows=192497 width=44) -> XN Seq Scan on sales (cost=0.00..1724.56 rows=172456 width=53)
次の例では、同じクエリ内の異なる種類の結合を示します。前の例と同じように、SALES と LISTING はマージ結合されますが、3 番目のテーブルである EVENT は、マージ結合の結果とハッシュ結合される必要があります。ハッシュ結合は、ここでもブロードキャストのコストを発生させます。
explain select * from sales, listing, event where sales.listid = listing.listid and sales.eventid = event.eventid; QUERY PLAN ---------------------------------------------------------------------------- XN Hash Join DS_BCAST_INNER (cost=109.98..3871130276.17 rows=172456 width=132) Hash Cond: ("outer".eventid = "inner".eventid) -> XN Merge Join DS_DIST_NONE (cost=0.00..6285.93 rows=172456 width=97) Merge Cond: ("outer".listid = "inner".listid) -> XN Seq Scan on listing (cost=0.00..1924.97 rows=192497 width=44) -> XN Seq Scan on sales (cost=0.00..1724.56 rows=172456 width=53) -> XN Hash (cost=87.98..87.98 rows=8798 width=35) -> XN Seq Scan on event (cost=0.00..87.98 rows=8798 width=35)
例: 結合、集計、およびソート
以下のクエリは、SALES および EVENT テーブルのハッシュ結合を実行し、次に集計およびソートオペレーションを実行して、グループ化された SUM 関数および ORDER BY 句を計上します。最初の Sort 演算子はコンピューティングノードで並行に実行されます。次に、Network 演算子は結果をリーダーノードに送ります。リーダーノードでは、Merge 演算子がソートされた最終的な結果を作成します。
explain select eventname, sum(pricepaid) from sales, event where sales.eventid=event.eventid group by eventname order by 2 desc; QUERY PLAN --------------------------------------------------------------------------------- XN Merge (cost=1002815366604.92..1002815366606.36 rows=576 width=27) Merge Key: sum(sales.pricepaid) -> XN Network (cost=1002815366604.92..1002815366606.36 rows=576 width=27) Send to leader -> XN Sort (cost=1002815366604.92..1002815366606.36 rows=576 width=27) Sort Key: sum(sales.pricepaid) -> XN HashAggregate (cost=2815366577.07..2815366578.51 rows=576 width=27) -> XN Hash Join DS_BCAST_INNER (cost=109.98..2815365714.80 rows=172456 width=27) Hash Cond: ("outer".eventid = "inner".eventid) -> XN Seq Scan on sales (cost=0.00..1724.56 rows=172456 width=14) -> XN Hash (cost=87.98..87.98 rows=8798 width=21) -> XN Seq Scan on event (cost=0.00..87.98 rows=8798 width=21)
データの再分散
結合の EXPLAIN 出力は、結合を容易にするためにクラスター周囲にデータを移動する方法も指定します。このデータの移動は、ブロードキャストまたは再分散のいずれかとすることができます。ブロードキャストでは、結合の一方の側からのデータ値は、各コンピューティングノードから他方の各コンピューティングノードにコピーされ、各コンピューティングノードはデータの完全なコピーで終了します。再分散では、特定のデータ値は現在のスライスから新しいスライス (別のノードにある場合があります) に送信されます。通常、データは再分散され、結合で該当する他方のテーブルの分散キーと一致します (その分散キーが結合する列の 1 つである場合)。いずれのテーブルにも、結合する列の 1 つに分散キーがない場合、両方のテーブルが分散されるか、内部テーブルが各ノードにブロードキャストされます。
EXPLAIN 出力は、内部テーブルと外部テーブルも参照します。内部テーブルが最初にスキャンされ、クエリプランの下部付近に表示されます。内部テーブルは、一致について調査されるテーブルです。通常、このテーブルはメモリに保持されますが、通常はハッシュのソーステーブルであり、可能な場合は、結合される 2 つのテーブルのうち小さい方になります。外部テーブルは、内部テーブルに対して一致させる行のソースです。通常はディスクから読み込まれます。クエリオプティマイザは、前回実行した ANALYZE コマンドから生成されたデータベース統計情報に基づいて内部テーブルと外部テーブルを選択します。クエリの FROM 句のテーブルの順序により、どのテーブルが内部でどのテーブルが外部かは決まりません。
結合を容易にするためにデータを移動する方法を特定するには、クエリプランで次の属性を使用します。
-
DS_BCAST_INNER
内部テーブル全体のコピーがすべてのコンピューティングノードにブロードキャストされます。
-
DS_DIST_ALL_NONE
内部テーブルは DISTSTYLE ALL を使用してすべてのノードにすでに分散されているため、再分散は不要です。
-
DS_DIST_NONE
テーブルは再分散されない。対応するスライスがノード間でのデータ移動なしで結合されるため、共存結合が可能となる。
-
DS_DIST_INNER
内部テーブルが再分散されます。
-
DS_DIST_OUTER
外部テーブルが再分散されます。
-
DS_DIST_ALL_INNER
外部テーブルで DISTSTYLE ALL が使用されるため、内部テーブル全体が単一スライスに再分散されます。
-
DS_DIST_BOTH
両方のテーブルが再分散されます。