EXCHANGE 演算子は、スレッド間でデータをやり取るための演算子です。
EXCHANGE 演算子は分散シナリオに適しており、通常はペアで使用されます。データソース側には OUT 演算子があり、ターゲット側には IN 演算子が配置されます。
EXCH-IN/OUT
EXCH-IN/OUT、すなわち EXCHANGE IN/EXCHANGE OUT は、複数のパーティション上のデータを集約し、クエリが実行されるホストノードに送信するために使用されます。
以下の例のように、次のクエリでは5つのパーティション(p0-p4)のデータにアクセスしています。そのうち、演算子1は演算子2が生成した出力を受け取り、データを出力します。また、演算子0は複数のパーティション上で演算子1が生成した出力を受け取り、結果を集計して出力します。
obclient> CREATE TABLE t15 (c1 INT, c2 INT) PARTITION BY HASH(c1) PARTITIONS 5;
Query OK, 0 rows affected
obclient> EXPLAIN SELECT * FROM t15;
+---------------------------------------------------------------------------+
| Query Plan |
+---------------------------------------------------------------------------+
| ============================================================= |
| |ID|OPERATOR |NAME |EST.ROWS|EST.TIME(us)| |
| ------------------------------------------------------------- |
| |0 |PX COORDINATOR | |1 |20 | |
| |1 |└─EXCHANGE OUT DISTR |:EX10000|1 |20 | |
| |2 | └─PX PARTITION ITERATOR| |1 |19 | |
| |3 | └─TABLE FULL SCAN |t15 |1 |19 | |
| ============================================================= |
| Outputs & filters: |
| ------------------------------------- |
| 0 - output([INTERNAL_FUNCTION(t15.c1, t15.c2)]), filter(nil), rowset=16 |
| 1 - output([INTERNAL_FUNCTION(t15.c1, t15.c2)]), filter(nil), rowset=16 |
| dop=1 |
| 2 - output([t15.c1], [t15.c2]), filter(nil), rowset=16 |
| force partition granule |
| 3 - output([t15.c1], [t15.c2]), filter(nil), rowset=16 |
| access([t15.c1], [t15.c2]), partitions(p[0-4]) |
| is_index_back=false, is_global_index=false, |
| range_key([t15.__pk_increment]), range(MIN ; MAX)always true |
+---------------------------------------------------------------------------+
上記の例の実行計画表示において、outputs & filters には EXCH-IN/OUT 演算子の出力情報が詳細に示されています。
| 情報名 | 意味 |
|---|---|
| PX COORDINATOR | 特殊なEXCHANGE IN演算子であり、リモートデータを取得するだけでなく、サブプランの実行スケジューリングも担当します。 |
| Output | この演算子が出力する式です。 |
| filter | この演算子におけるフィルタ条件です。例ではEXCH-IN/OUT演算子にfilterが設定されていないため、nilとなります。 |
EXCH-IN/OUT (REMOTE)
EXCH-IN/OUT (REMOTE) 演算子は、リモートのデータ(単一パーティションのデータ)をローカルにプルするために使用されます。
以下の例のように、Aマシン上にパーティション化されていないテーブルを作成し、Bマシンでクエリを実行してそのテーブルのデータを読み取ります。この場合、読み取るデータがリモートにあるため、実行計画ではリモートのデータをプルするために演算子0と演算子1が割り当てられています。演算子1はAマシン上で実行され、t14 テーブルのデータを読み取り、データをアウトプットします。演算子0はBマシン上で実行され、演算子1が生成した出力を受信します。
obclient> CREATE TABLE t14 (c1 INT, c2 INT);
Query OK, 0 rows affected
obclient> EXPLAIN SELECT * FROM t14;
+--------------------------------------------------------------------+
| Query Plan |
+--------------------------------------------------------------------+
| ===================================================== |
| |ID|OPERATOR |NAME|EST.ROWS|EST.TIME(us)| |
| ----------------------------------------------------- |
| |0 |EXCHANGE IN REMOTE | |1 |5 | |
| |1 |└─EXCHANGE OUT REMOTE| |1 |4 | |
| |2 | └─TABLE FULL SCAN |t14 |1 |3 | |
| ===================================================== |
| Outputs & filters: |
| ------------------------------------- |
| 0 - output([t14.C1], [t14.C2]), filter(nil) |
| 1 - output([t14.C1], [t14.C2]), filter(nil) |
| 2 - output([t14.C1], [t14.C2]), filter(nil), rowset=16 |
| access([t14.C1], [t14.C2]), partitions(p0) |
| is_index_back=false, is_global_index=false, |
| range_key([t14.__pk_increment]), range(MIN ; MAX)always true |
+--------------------------------------------------------------------+
上記の例の実行計画表示における outputs & filters は、EXCH-IN/OUT (REMOTE) 演算子の出力情報を詳細に示しており、フィールドの意味は EXCH-IN/OUT 演算子と同じです。
EXCH-IN/OUT (PKEY)
EXCH-IN/OUT (PKEY) 演算子は、データの再パーティションに使用されます。通常、二項演算子で使用され、一方のサブノードのデータをもう一方のサブノードのパーティショニング方式に従って再パーティションします。
以下の例では、このクエリは2つのパーティションテーブルのデータを結合しています。実行計画では、s15 テーブルのデータを t13 テーブルのパーティショニング方式に従って再パーティションします。4番目の演算子の入力は s15 テーブルのスキャン結果であり、s15 テーブルの各行について、この演算子は t13 テーブルのデータパーティションとクエリの結合条件に基づいて、どのノードにデータを送信するかを決定します。
さらに、3番目の演算子が EXCHANGE IN DISTR であることがわかります。これは特殊な EXCHANGE IN 演算子であり、複数のパーティションのデータを集計する際に一定のマージソートを行うために使用されます。この実行計画では、3番目の演算子が受け取る各パーティションのデータは c1 列に従って順序付けられており、受け取った各データに対してマージソートを行うことで、結果出力も c1 列に従って順序付けられるようになります。
obclient> CREATE TABLE t13 (c1 INT, c2 INT) PARTITION BY HASH(c1) PARTITIONS 5;
Query OK, 0 rows affected
obclient> CREATE TABLE s15 (c1 INT PRIMARY KEY, c2 INT) PARTITION BY HASH(c1) PARTITIONS 4;
Query OK, 0 rows affected
obclient> EXPLAIN SELECT * FROM s15, t13 WHERE s15.c1 = t13.c1;
+-------------------------------------------------------------------------------------------+
| Query Plan |
+-------------------------------------------------------------------------------------------+
| ===================================================================== |
| |ID|OPERATOR |NAME |EST.ROWS|EST.TIME(us)| |
| --------------------------------------------------------------------- |
| |0 |PX COORDINATOR | |1 |38 | |
| |1 |└─EXCHANGE OUT DISTR |:EX10001|1 |37 | |
| |2 | └─HASH JOIN | |1 |36 | |
| |3 | ├─EXCHANGE IN DISTR | |1 |17 | |
| |4 | │ └─EXCHANGE OUT DISTR (PKEY)|:EX10000|1 |16 | |
| |5 | │ └─PX PARTITION ITERATOR | |1 |16 | |
| |6 | │ └─TABLE FULL SCAN |s15 |1 |16 | |
| |7 | └─PX PARTITION ITERATOR | |1 |19 | |
| |8 | └─TABLE FULL SCAN |t13 |1 |19 | |
| ===================================================================== |
| Outputs & filters: |
| ------------------------------------- |
| 0 - output([INTERNAL_FUNCTION(s15.c1, s15.c2, t13.c1, t13.c2)]), filter(nil), rowset=16 |
| 1 - output([INTERNAL_FUNCTION(s15.c1, s15.c2, t13.c1, t13.c2)]), filter(nil), rowset=16 |
| dop=1 |
| 2 - output([s15.c1], [t13.c1], [s15.c2], [t13.c2]), filter(nil), rowset=16 |
| equal_conds([s15.c1 = t13.c1]), other_conds(nil) |
| 3 - output([s15.c1], [s15.c2]), filter(nil), rowset=16 |
| 4 - output([s15.c1], [s15.c2]), filter(nil), rowset=16 |
| (#keys=1, [s15.c1]), dop=1 |
| 5 - output([s15.c1], [s15.c2]), filter(nil), rowset=16 |
| force partition granule |
| 6 - output([s15.c1], [s15.c2]), filter(nil), rowset=16 |
| access([s15.c1], [s15.c2]), partitions(p[0-3]) |
| is_index_back=false, is_global_index=false, |
| range_key([s15.c1]), range(MIN ; MAX)always true |
| 7 - output([t13.c1], [t13.c2]), filter(nil), rowset=16 |
| affinitize, force partition granule |
| 8 - output([t13.c1], [t13.c2]), filter(nil), rowset=16 |
| access([t13.c1], [t13.c2]), partitions(p[0-4]) |
| is_index_back=false, is_global_index=false, |
| range_key([t13.__pk_increment]), range(MIN ; MAX)always true |
+-------------------------------------------------------------------------------------------+
上記の例の実行計画表示における outputs & filters は、EXCH-IN/OUT (PKEY) 演算子の出力情報を詳細に示しています。
| 情報名 | 意味 |
|---|---|
| PX COORDINATOR | 特殊なEXCHANGE IN演算子であり、リモートデータをプルバックするだけでなく、サブプランの実行スケジューリングも担当します。 |
| Output | この演算子が出力する式です。 |
| filter | この演算子上のフィルター条件です。例ではEXCH-IN/OUT(PKEY)演算子にfilterが設定されていないため、nilとなります。 |
| pkey | どの列に基づいて再パーティション化を行うかを指定します。例えば、#keys=1, [s15.c1]はc1列に基づいて再パーティション化を行うことを意味します。 |
EXCH-IN/OUT (HASH)
EXCH-IN/OUT (HASH) 演算子は、データに対して一連のハッシュ関数を用いて再パーティション化を行うために使用されます。
以下の例の実行計画では、3-5番および7-8番がハッシュ再パーティションを使用する2つの EXCHANGE 演算子です。これら2つの演算子の役割は、t12 テーブルと s14 テーブルのデータを新しいハッシュ関数に基づいて複数の部分に分散させることです。この例でのハッシュ列は t12.c2 と s14.c2 であり、これにより c2 列の値が同じ行が同一の部分に配置されることを保証します。再パーティション化後のデータに基づき、2番目の演算子 HASH JOIN は各データ部分に対して t12.c2= s14.c2 に従って結合を行います。
また、クエリで並列度2が実行されているため、計画には dop = 2 が示されています(DOPはDegree of Parallelismの略です)。
obclient> CREATE TABLE t12 (c1 INT, c2 INT) PARTITION BY HASH(c1) PARTITIONS 4;
Query OK, 0 rows affected
obclient> CREATE TABLE s14 (c1 INT, c2 INT) PARTITION BY HASH(c1) PARTITIONS 4;
Query OK, 0 rows affected
obclient> EXPLAIN SELECT /*+PARALLEL(2) LEADING(t12 s14) USE_HASH(s14) PQ_DISTRIBUTE(s14 HASH HASH)*/ * FROM t12, s14 WHERE t12.c2 = s14.c2;
+-------------------------------------------------------------------------------------------+
| Query Plan |
+-------------------------------------------------------------------------------------------+
| ===================================================================== |
| |ID|OPERATOR |NAME |EST.ROWS|EST.TIME(us)| |
| --------------------------------------------------------------------- |
| |0 |PX COORDINATOR | |1 |18 | |
| |1 |└─EXCHANGE OUT DISTR |:EX10002|1 |17 | |
| |2 | └─HASH JOIN | |1 |17 | |
| |3 | ├─EXCHANGE IN DISTR | |1 |9 | |
| |4 | │ └─EXCHANGE OUT DISTR (HASH)|:EX10000|1 |8 | |
| |5 | │ └─PX BLOCK ITERATOR | |1 |8 | |
| |6 | │ └─TABLE FULL SCAN |t12 |1 |8 | |
| |7 | └─EXCHANGE IN DISTR | |1 |9 | |
| |8 | └─EXCHANGE OUT DISTR (HASH)|:EX10001|1 |8 | |
| |9 | └─PX BLOCK ITERATOR | |1 |8 | |
| |10| └─TABLE FULL SCAN |s14 |1 |8 | |
| ===================================================================== |
| Outputs & filters: |
| ------------------------------------- |
| 0 - output([INTERNAL_FUNCTION(t12.c1, t12.c2, s14.c1, s14.c2)]), filter(nil), rowset=16 |
| 1 - output([INTERNAL_FUNCTION(t12.c1, t12.c2, s14.c1, s14.c2)]), filter(nil), rowset=16 |
| dop=2 |
| 2 - output([t12.c2], [s14.c2], [t12.c1], [s14.c1]), filter(nil), rowset=16 |
| equal_conds([t12.c2 = s14.c2]), other_conds(nil) |
| 3 - output([t12.c2], [t12.c1]), filter(nil), rowset=16 |
| 4 - output([t12.c2], [t12.c1]), filter(nil), rowset=16 |
| (#keys=1, [t12.c2]), dop=2 |
| 5 - output([t12.c1], [t12.c2]), filter(nil), rowset=16 |
| 6 - output([t12.c1], [t12.c2]), filter(nil), rowset=16 |
| access([t12.c1], [t12.c2]), partitions(p[0-3]) |
| is_index_back=false, is_global_index=false, |
| range_key([t12.__pk_increment]), range(MIN ; MAX)always true |
| 7 - output([s14.c2], [s14.c1]), filter(nil), rowset=16 |
| 8 - output([s14.c2], [s14.c1]), filter(nil), rowset=16 |
| (#keys=1, [s14.c2]), dop=2 |
| 9 - output([s14.c1], [s14.c2]), filter(nil), rowset=16 |
| 10 - output([s14.c1], [s14.c2]), filter(nil), rowset=16 |
| access([s14.c1], [s14.c2]), partitions(p[0-3]) |
| is_index_back=false, is_global_index=false, |
| range_key([s14.__pk_increment]), range(MIN ; MAX)always true |
+-------------------------------------------------------------------------------------------+
その中で、PX PARTITION ITERATOR 演算子はパーティション単位でデータをイテレーションするために使用されます。詳細については、GI を参照してください。
上記の例の実行計画表示における outputs & filters は、EXCH-IN/OUT (HASH) 演算子の出力情報を以下のように詳細に示しています:
| 情報名 | 意味 |
|---|---|
| PX COORDINATOR | 特殊な EXCHANGE IN 演算子であり、リモートデータを取得するだけでなく、サブプランの実行スケジューリングも担当します。 |
| Output | この演算子が出力する式です。 |
| filter | この演算子におけるフィルタ条件です。例では EXCH-IN/OUT (HASH) 演算子に filter が設定されていないため、nil となります。 |
| pkey | ハッシュ再パーティションを行う列を指定します。例えば、#keys=1, [s14.c2] は c2 列に基づいてハッシュ再パーティションを行うことを意味します。 |
EXCH-IN/OUT (BC2HOST)
EXCH-IN/OUT (BC2HOST) 演算子は、入力データに対して BC2HOST の方法を用いて再パーティション化を行うために使用され、データを他のスレッドにブロードキャストします。
以下の例の実行計画では、3-4番が BC2HOST を使用した再パーティション化方式の EXCHANGE 演算子です。この演算子は、テーブル t11 のデータを各スレッドにブロードキャストし、テーブル s13 の各パーティションのデータは、ブロードキャストされた t11 テーブルのデータと結合を試みます。
obclient> CREATE TABLE t11 (c1 INT, c2 INT) PARTITION BY HASH(c1) PARTITIONS 4;
Query OK, 0 rows affected
obclient> CREATE TABLE s13 (c1 INT, c2 INT) PARTITION BY HASH(c1) PARTITIONS 4;
Query OK, 0 rows affected
obclient> INSERT INTO s VALUES (1, 1), (2, 2), (3, 3), (4, 4);
Query OK, 1 rows affected
obclient> EXPALIN SELECT /*+PARALLEL(2) */ * FROM t11, s13 WHERE t11.c2 = s13.c2;
+-------------------------------------------------------------------------------------------+
| Query Plan |
+-------------------------------------------------------------------------------------------+
| ======================================================================== |
| |ID|OPERATOR |NAME |EST.ROWS|EST.TIME(us)| |
| ------------------------------------------------------------------------ |
| |0 |PX COORDINATOR | |1 |18 | |
| |1 |└─EXCHANGE OUT DISTR |:EX10001|1 |17 | |
| |2 | └─SHARED HASH JOIN | |1 |17 | |
| |3 | ├─EXCHANGE IN DISTR | |1 |9 | |
| |4 | │ └─EXCHANGE OUT DISTR (BC2HOST)|:EX10000|1 |8 | |
| |5 | │ └─PX BLOCK ITERATOR | |1 |8 | |
| |6 | │ └─TABLE FULL SCAN |t11 |1 |8 | |
| |7 | └─PX BLOCK ITERATOR | |4 |8 | |
| |8 | └─TABLE FULL SCAN |s13 |4 |8 | |
| ======================================================================== |
| Outputs & filters: |
| ------------------------------------- |
| 0 - output([INTERNAL_FUNCTION(t11.c1, t11.c2, s13.c1, s13.c2)]), filter(nil), rowset=16 |
| 1 - output([INTERNAL_FUNCTION(t11.c1, t11.c2, s13.c1, s13.c2)]), filter(nil), rowset=16 |
| dop=2 |
| 2 - output([t11.c2], [s13.c2], [t11.c1], [s13.c1]), filter(nil), rowset=16 |
| equal_conds([t11.c2 = s13.c2]), other_conds(nil) |
| 3 - output([t11.c2], [t11.c1]), filter(nil), rowset=16 |
| 4 - output([t11.c2], [t11.c1]), filter(nil), rowset=16 |
| dop=2 |
| 5 - output([t11.c1], [t11.c2]), filter(nil), rowset=16 |
| 6 - output([t11.c1], [t11.c2]), filter(nil), rowset=16 |
| access([t11.c1], [t11.c2]), partitions(p[0-3]) |
| is_index_back=false, is_global_index=false, |
| range_key([t11.__pk_increment]), range(MIN ; MAX)always true |
| 7 - output([s13.c1], [s13.c2]), filter(nil), rowset=16 |
| 8 - output([s13.c1], [s13.c2]), filter(nil), rowset=16 |
| access([s13.c1], [s13.c2]), partitions(p[0-3]) |
| is_index_back=false, is_global_index=false, |
| range_key([s13.__pk_increment]), range(MIN ; MAX)always true |
+-------------------------------------------------------------------------------------------+
上記の例の実行計画に示されている outputs & filters は、EXCH-IN/OUT (BC2HOST) 演算子の情報を詳細に示しており、フィールドの意味は EXCH-IN/OUT 演算子と同じです。