Aurora DSQL EXPLAIN プランのバッチ処理されたネストループ結合
Aurora DSQL がストレージから効率的にスキャンできる内部入力に対して、クエリが小さな外部入力を結合する場合、Aurora DSQL は Nested Loop (Batched Join) プランを選択できます。この結合タイプは、内部側をプローブする前に複数の外部行をグループ化することで、コンピューティングレイヤーとストレージレイヤー間のラウンドトリップを削減します。
標準のネストループは、外部の行を一度に 1 つずつ処理し、行ごとに内部スキャンを再度実行します。バッチ処理されたネストループ結合は、外部の行をバッチとして収集し、バッチ全体に対して内部スキャン処理を構築してから、返された内部の行を一致する外部の行に結合します。
バッチ処理されたネストループ結合の仕組み
-
Aurora DSQL は、結合の外側から行のバッチを読み取ります。
-
内部側が
Index ScanまたはIndex Only Scanであり、外部側にパラメータ化されたIndex Condがある場合、Aurora DSQL は各外部行の条件をバインドし、結果のインデックススキャンキーを 1 つのバッチ処理された内部スキャンにまとめます。その他の結合述語は内部スキャンを駆動しません。 -
外部パラメータ化された
Index CondがないIndex ScanまたはIndex Only Scanは、Full ScanまたはSequential Scanのように動作します。Aurora DSQL には結合するパラメータ化されたインデックススキャンキーがなく、外部行ごとに 1 つの内部スキャンを実行する代わりに、外部バッチごとに 1 つの内部スキャンを実行します。 -
Aurora DSQL は、バッチ処理のために内部側から到着した行をストリーミングします。結合前に、バッチの内部結果全体をマテリアライズすることはありません。
-
内部の
Index ScanまたはIndex Only Scanに外部パラメータ化されたIndex Condがある場合、Aurora DSQL はRecheck Condを使用して、返された各内部行をバッチ内の該当する外部行と照合します。その後、Aurora DSQL は残りの結合述語を適用します。外部パラメータ化されたIndex Condがない場合、Aurora DSQL は結合述語を直接適用し、ストリーミングされた各内部行を外部バッチと照合します。 -
左結合と反結合の場合、Aurora DSQL は一致した外部行を追跡し、バッチの内部スキャンが完了した後に一致しなかった行を出力します。
このアプローチは、内部側がパラメータ化された Index Cond を持つインデックススキャンである場合に最も役立ちます。組み合わされたスキャンキーにより、外部バッチ全体に対してターゲットを絞ったストレージアクセスが可能になるためです。
Aurora DSQL がこの結合を使用する場合
Aurora DSQL がバッチ処理されたネストループ結合を検討するのは、結合の内側が物理スキャンノード (Index Scan、Index Only Scan、Full
Scan、または Sequential Scan) であり、バッチ処理のコストが、外部行ごとに内部スキャンを実行するよりも低くなると予想される場合です。実際には、通常、外部入力が小さく、外部側でパラメータ化された Index Cond を使用して内部インデックススキャンを実行したときに、最大の利点が得られます。
ハッシュ結合やマージ結合など、別のプラン形状の方がコストが低い場合、Aurora DSQL は代わりにそのプランを選択します。チューニング中にプラン形状を比較するには、現在のセッションでバッチ処理されたネストループ結合を無効にすることができます。
SET dsql.enable_batched_nestloop = off;
バッチ処理されたネストループ結合の出力を読み取る方法
次のクエリでは、Aurora DSQL EXPLAIN プランの読み取り のサンプル transaction テーブルと account テーブルを使用します。
EXPLAIN SELECT t.account_id, a.balance FROM transaction t LEFT JOIN account a ON t.account_id = a.customer_id AND a.balance > CASE WHEN t.description LIKE 'fee%' THEN 0 ELSE 100 END WHERE t.transaction_date >= '2025-01-01' AND (a.status = 'active' OR a.customer_id IS NULL) ORDER BY t.account_id;
EXPLAIN プランには、次のような出力が含まれる場合があります。
Sort
Sort Key: t.account_id
-> Nested Loop (Batched Join)
Filter: (((status)::text = 'active'::text) OR (customer_id IS NULL))
Join Type: Left
Recheck Cond: (customer_id = t.account_id)
Join Filter: (balance > CASE WHEN (t.description ~~ 'fee%'::text) THEN '0'::numeric ELSE '100'::numeric END)
-> Full Scan (btree-table) on transaction t
-> Storage Scan on transaction t
Filters: (transaction_date >= '2025-01-01 00:00:00'::timestamp without time zone)
-> B-Tree Scan on transaction t
-> Index Only Scan using idx1 on account a
Index Cond: (customer_id = t.account_id)
Nested Loop (Batched Join)-
Aurora DSQL が結合の内側を調べる前に、外部行をバッチ処理していることを示します。
Index Cond-
現在のバッチの内側をプローブするために使用される述語を表示します。内側が
Index ScanまたはIndex Only Scanの場合、この述語は結合の外側の列を参照することがよくあります。 Filter-
結合の結果に適用された条件を表示します。この例では、
WHERE (a.status = 'active' OR a.customer_id IS NULL)条件がここに表示されます。標準の SQL 左結合セマンティクスでは、この条件を内部スキャンにプッシュすることはできません。プッシュするとクエリ結果が変わるためです。Aurora DSQL では、これが結合ノードのフィルターとして表示されます。対照的に、t.transaction_dateの条件は外部テーブルのみを参照するため、外部物理スキャンの下に表示されます。 Join Type-
Left、Semi、Antiなど、バッチ結合の結合セマンティクスを表示します。 Recheck Cond-
内部側が
Index ScanまたはIndex Only Scanで、結合の外部側でパラメータ化されたIndex Condが少なくとも 1 つある場合にのみ表示されます。返される内部行ごとに、Aurora DSQL はバッチ内のすべての外部行に対して再チェックを実行し、どの外部行がそのプローブを生成したか、どの外部行をその内部行と結合する必要があるかを判断します。 Join Filter-
バッチ内の候補となる外部行に対して内部行を照合した後に Aurora DSQL が評価する結合述語を示します。これらの述語は、どの行ペアが結合されるかに影響しますが、
Index Condのようにストレージプローブを駆動するわけではありません。 - 直前のソートノード
-
クエリに順序付けられた出力が必要な場合に表示されることがあります。バッチ処理されたネストループ結合は、標準のネストループと同じ出力順序を保証しないため、Aurora DSQL は最終的な結果セットを返す前に明示的なソートを追加することがあります。