このトピックでは、PolarDB-Xのオプティマイザおよび実行プログラムがJOIN操作を処理する方法について説明します。 JOIN操作は、複数のテーブルの行を、これらのテーブル間の1つ以上の共通の列に基づいて結合する操作です。
JOIN操作とは何ですか?
JOIN操作は、SQLクエリで一般的に使用されます。 JOINのセマンティクスは、2つのテーブルのデカルト積を計算し、フィルタ条件を満たすデータを保持することと同等です。 ほとんどの場合、等結合が使用される。 等結合は、指定された列の値に基づいて2つのテーブルを結合するために使用されます。
サブクエリは、別のSQLクエリ内にネストされたクエリです。 サブクエリの結果は外部クエリに渡され、外部クエリの処理に使用されます。 サブクエリは、SQL文のさまざまなコンポーネントで使用できます。 たとえば、サブクエリは、SELECT句の出力データ、FROM句の入力ビュー、またはWHERE句のフィルター条件として使用できます。
このトピックで説明するJOIN操作はプッシュダウンできません。 ストレージ層のMySQLは、LogicalViewにプッシュダウンできるJOIN操作の実行方法を決定します。
JOIN操作のタイプ
PolarDB-Xは、次の一般的なJOIN操作タイプをサポートします。内部結合、左外部結合、および右外部結合です。 次の例は、さまざまな種類のJOIN操作を示しています。
/* インナー参加 * /
SELECT * FROM A, B WHERE A.key = B.key;
/* 左アウタージョイン * /
SELECT * からA.key = B.key;
/* 右アウター参加 * /
* 右の外側からの選択A.key = B.key;
PolarDB-Xは、セミ結合とアンチ結合もサポートしています。 セミ結合とアンチ結合はSQLでは記述できません。 これらは、EXISTSまたはIN条件内にネストされたサブクエリを使用して実装されます。 次のコードブロックは、セミ結合とアンチ結合の例を示しています。
/* セミ参加-1 * /
SELECT * FROM Emp.DeptName IN (Emp.DeptName IN)
部門からの部門名を選択
)
/* セミ参加-2 * /
SELECT * FROM Emp WHERE EXISTS (
SELECT * FROM DeptName = Dept.DeptName
)
/* アンチ参加-1 * /
Emp.DeptNameが入力されていない場所でEmpから * を選択します (
部門からの部門名を選択
)
/* アンチ参加-2 * /
存在しないエンプから * を選択します (
SELECT * FROM DeptName = Dept.DeptName
)
アルゴリズムを結合する
PolarDB-Xは、ネストループ結合、ハッシュ結合、ソートマージ結合、ルックアップ結合など、複数の結合アルゴリズムをサポートしています。 Lookup joinはBKA joinとも呼ばれます。
ネストループ結合 (NLJoin)
ネストされたループ結合は、非等結合に一般的に使用されます。 ネストされたループ結合は、次のように機能します。
内部テーブルからすべての行を選択し、その行をメモリにキャッシュします。 内部テーブルは、ほとんどの場合、より少ないデータを含む右側のテーブルを参照します。
外側のテーブル全体をスキャンします。 外側のテーブルの各行について:
各行は、メモリにキャッシュされた内部テーブルの行にマッピングされます。
結果セットを作成し、結果セットが結合条件を満たすかどうかを確認し、条件が満たされている場合は結果セットを返します。
次のコードブロックは、ネストされたループ結合の例を示します。
> EXPLAIN SELECT * FROM partsupp、サプライヤーWHERE ps_suppkey < s_suppkey; NlJoin(condition="ps_suppkey < s_suppkey", type="inner") 収集 (concurrent=true) LogicalView(tables="partsupp_[0-7]", shardCount=8, sql="SELECT * FROM 'partsupp' AS 'partsupp'") 収集 (concurrent=true) LogicalView(tables="supplier_[0-7]", shardCount=8, sql="SELECT * FROM 'supplier' AS 'supplier'")
ほとんどの場合、ネストされたループ結合は最も効率の悪い結合アルゴリズムです。 ネストされたループ結合は、次のシナリオでのみ使用できます。結合は、上記の例に示すように非等価結合であるか、内部テーブルに少量のデータが含まれています。 次のヒントを使用して、PolarDB-Xにネストされたループ結合を強制的に使用し、結合順序を指定できます。
/* + TDDL:NL_JOIN(outer_table、inner_table)*/ SELECT...
次の例に示すように、複数のテーブルを結合した結果をinner_tableまたはouter_tableとして使用することもできます。
/* + TDDL:NL_JOIN((outer_table_a, outer_table_b), (inner_table_c, inner_table_d))*/ SELECT...
ハッシュ参加
ハッシュ結合は、等結合に最も一般的に使用される結合アルゴリズムの1つです。 ハッシュ結合は次のように機能します。
内部テーブルからすべての行を選択し、メモリに格納されているハッシュテーブルに行を書き込みます。 内部テーブルは、ほとんどの場合、より少ないデータを含む右側のテーブルを参照します。
外側のテーブル全体をスキャンします。 外側のテーブルの各行について:
等価条件の結合キーに対してハッシュテーブルをスキャンし、同じ結合キーを持つ0〜N行を選択します。
結果セットを作成し、結果セットが結合条件を満たすかどうかを確認し、条件が満たされている場合は結果セットを返します。
次のコードブロックは、ハッシュ結合の例を示しています。
> EXPLAIN SELECT * FROM partsupp、サプライヤーWHERE ps_suppkey = s_suppkey; HashJoin(condition="ps_suppkey = s_suppkey", type="inner") 収集 (concurrent=true) LogicalView(tables="partsupp_[0-7]", shardCount=8, sql="SELECT * FROM 'partsupp' AS 'partsupp'") 収集 (concurrent=true) LogicalView(tables="supplier_[0-7]", shardCount=8, sql="SELECT * FROM 'supplier' AS 'supplier'")
ハッシュ結合は、大量のデータを結合する複雑なクエリで一般的に使用されますが、インデックス検索では最適化できません。 この場合、ハッシュ結合が最適なアルゴリズムである。 上記の例では、システムはpartsuppテーブルとsupplierテーブルに対して完全なテーブルスキャンを実行する必要があります。 これは、大量のデータを伴う。 したがって、ハッシュ結合はこのシナリオに適しています。 ハッシュ結合の内部テーブルは、メモリに格納されたハッシュテーブルを作成するために使用される。 内部テーブルに含まれるデータが外部テーブルよりも少ないことを確認します。 ほとんどの場合、オプティマイザは最適な結合順序を自動的に選択できます。
次のヒントを使用して、PolarDB-Xにハッシュ結合を強制的に使用し、結合順序を指定することもできます。
/* + TDDL:HASH_JOIN(table_outer, table_inner)*/ SELECT...
ルックアップ参加 (BKAJoin)
ルックアップ結合は、等結合のために一般的に使用される別の結合アルゴリズムであり、少量のデータが関与するシナリオで一般的に使用される。 ルックアップ結合は次のように機能します。
外側のテーブル全体をスキャンします。 外側のテーブルは、ほとんどの場合、より少ないデータを含む左側のテーブルを指します。 外部テーブルの各バッチ (たとえば、1,000行ごと)
バッチ内の行の結合キーを使用するIN (....) 条件を作成し、その条件を内部テーブルクエリに追加します。
内部テーブルクエリを実行して、結合条件を満たす行を選択します。
ハッシュテーブルに基づいて、外部テーブルの各行を内部テーブルの行にマップし、内部テーブルと外部テーブルの行をマージして、結果を返します。
次のコードブロックは、ルックアップ結合 (BKA結合) の例を示しています。
> EXPLAIN SELECT * FROM partsupp、サプライヤーWHERE ps_suppkey = s_suppkeyおよびps_partkey = 123;
BKAJoin(condition="ps_suppkey = s_suppkey" 、type="inner")
LogicalView(tables="partsupp_3", sql="SELECT * FROM 'partsupp' AS 'partsupp' WHERE ('ps_partkey' = ?)")
収集 (concurrent=true)
LogicalView(tables="supplier_[0-7]", shardCount=8, sql="SELECT * FROM 'supplier'AS 'supplier'WHERE ('s_suppkey' IN ('?')))")
ルックアップ結合は、外部テーブルが少量のデータを含む場合に適している。 上記の例では、フィルター条件ps_partkey = 123のため、左側のテーブルpartsuppから数行のみが選択されています。 さらに、s_suppkey In ( ... ) 条件は、右側のテーブルの主キーインデックスと一致します。 これにより、ルックアップ結合のコストが低減される。
次のヒントを使用して、PolarDB-Xにルックアップ結合を強制し、結合順序を指定できます。
/* + TDDL:BKA_JOIN(table_outer, table_inner)*/ SELECT...
ルックアップ結合の内部テーブルは、複数のテーブルを結合した結果ではなく、単一のテーブルでなければなりません。
並べ替え-マージ参加
ソート − マージ結合は、等結合のための別の結合アルゴリズムである。 ソートマージ結合は、左右のテーブルの入力行の順序に依存します。 入力行は、結合キーに基づいてソートする必要があります。 ソートマージ結合は次のように機能します。
MergeSortまたはMemSortを使用して入力行をソートします。
次の方法を使用して、左右のテーブルの入力行を比較します。
現在の左の行の結合キーが現在の右の行の結合キーよりも小さい場合、次の左の行に対して現在の右の行を一致させる。
現在の右行の結合キーが現在の左行の結合キーよりも小さい場合、現在の左行を次の右行と照合します。
2つの行に同じ結合キーがあり、結合条件が満たされている場合、左行と右行を結合し、結果を返します。
次のコードブロックは、Sort-Merge Joinの例を示しています。
> EXPLAIN SELECT * FROM partsupp、サプライヤーWHERE ps_suppkey = s_suppkey ORDER BY s_suppkey;
SortMergeJoin(condition="ps_suppkey = s_suppkey", type="inner")
MergeSort(sort="ps_suppkey ASC")
LogicalView(tables="QIMU_0000_GROUP,QIMU_0001_GROUP.partsupp_[0-7]", shardCount=8, sql="SELECT * FROM 'partsupp' AS 'partsupp' ORDER BY 'ps_suppkey'")
MergeSort(sort="s_suppkey ASC")
LogicalView(tables="QIMU_0000_GROUP,QIMU_0001_GROUP.supplier_[0-7]", shardCount=8, sql="SELECT * FROM 'supplier'AS 'supplier'ORDER's_suppkey'")
上記の実行計画のMergeSort演算子と、プッシュダウンされたORDER BY演算子に注意してください。 これらの演算子は、ソートマージ結合の左右のテーブルの入力行が結合キーs_suppkey (ps_suppkey) に基づいてソートされるようにするために使用されます。
ソートマージ結合は、入力行を最初にソートする必要があるため、最適な結合アルゴリズムではありません。 しかしながら、ユーザは、前の例に示すように、結合キーに基づいてクエリ結果をソートしたい場合がある。 この場合、ソート − マージ結合が最適な選択である。
次のヒントを使用して、PolarDB-Xにソートマージ結合を強制的に使用できます。
/* + TDDL:SORT_MERGE_JOIN(table_a, table_b)*/ SELECT...
注文に参加する
複数のテーブルが結合されるシナリオでは、オプティマイザはテーブルを結合する順序を決定する必要があります。 これは、結合順序が中間結果セットのサイズと実行プランのコストに影響するためです。
たとえば、JOIN操作は4つのテーブルに対して実行され、JOIN操作はプッシュダウンされません。 この場合、以下の結合ツリーが適用可能である。 さらに、4つのテーブルは最大24の方法でソートできます。 その結果、最大72の異なる結合注文が利用可能である。
PolarDB-Xは、適応戦略を使用して、N個のテーブルで実行される特定のJOIN操作の最適な実行計画を作成します。
JOIN操作がプッシュダウンされず、Nの値が小さい場合、最適な実行計画を作成するためにふさふさ木が使用されます。
JOIN操作がプッシュダウンされず、Nの値が大きい場合、ジグザグまたは左の深さのツリーを使用して最適な実行プランを作成します。 これにより、計算回数とコストが削減されます。
PolarDB-Xは、コストベースのオプティマイザを使用して、コストが最も低い結合順序を選択します。 詳細については、「クエリオプティマイザー」をご参照ください。
さらに、異なる結合アルゴリズムは、左右のテーブルに対して異なる好みを有する。 たとえば、ハッシュ結合の右側のテーブルは内部テーブルであり、ハッシュテーブルを作成するために使用されます。 したがって、データが少ないテーブルを正しいテーブルとして指定します。 コストベースのオプティマイザも同様の好みを持っています。
PolarDB-Xは、さまざまな結合アルゴリズムをサポートします。 オプティマイザは、収集された統計に基づいて適切な結合アルゴリズムを選択する。 次の表に、結合アルゴリズムが適用可能なシナリオを示します。
結合アルゴリズム | シナリオ |
NLJoin | 非エクイ結合。 |
HashJoin | ハッシュ結合は、データが大きく歪んでいない限り、ほとんどの等結合に使用されます。 |
BKAJoin | 外部テーブルは少量のデータを含む。 内部テーブルには大量のデータが含まれています。 |
並べ替え-マージ-参加 | データはひどく歪んでいるか、データはすでにソートされています。 |