パラレル実行(Parallel Execution)とは、大きなタスクを複数の小さなタスクに分割し、マルチスレッドまたはプロセスを用いて並行処理することで、より多くのCPUおよびI/Oリソースを活用し、応答時間を短縮する手法です。OceanBaseでは、以下の3種類のパラレル実行がサポートされています:
- パラレルクエリ(Parallel Query)
- パラレルDDL(Parallel DDL)
- パラレルDML(Parallel DML)
本記事では、**パラレルクエリ(Parallel Query)**について説明します。
パラレルクエリを開始する方法は以下の2通りあります:
PARALLELヒントを使用して、並列度(DOP)を指定してパラレルクエリを開始します。クエリ対象のパーティション数が1より大きいパーティションテーブルに対しては、自動的にパラレルクエリが開始されます。
パーティションテーブルのパラレルクエリを有効にする
OceanBaseにおけるパーティションテーブルのパラレルクエリのルールは以下のとおりです:
自動並列化ルール
クエリの ターゲットパーティション数 > 1 の場合、システムは自動的にパラレルクエリを有効にし、デフォルトの DOP(並列度) は 1 です。
例:全テーブルスキャンのデフォルト並列
以下の例のように、パーティションテーブル ptable を作成します:
CREATE TABLE ptable(c1 INT, c2 INT) PARTITION BY HASH(c1) PARTITIONS 16;
ptable に対して全テーブルデータのスキャン操作を実行し、EXPLAIN コマンドで生成された実行計画を確認します:
EXPLAIN SELECT * FROM ptable;
実行計画出力:
Query Plan:
=======================================================
|ID|OPERATOR |NAME |EST. ROWS|COST |
-------------------------------------------------------
|0 |EXCHANGE IN DISTR | |1600000 |1246946|
|1 | EXCHANGE OUT DISTR |:EX10000|1600000 |1095490|
|2 | PX PARTITION ITERATOR| |1600000 |1095490|
|3 | TABLE SCAN |ptable |1600000 |1095490|
=======================================================
Outputs & filters:
-------------------------------------
0 - output([ptable.c1], [ptable.c2]), filter(nil)
1 - output([ptable.c1], [ptable.c2]), filter(nil), dop=1
2 - output([ptable.c1], [ptable.c2]), filter(nil)
3 - output([ptable.c1], [ptable.c2]), filter(nil),
access([ptable.c1], [ptable.c2]), partitions(p[0-15])
例の分析:
- デフォルトでは
DOP=1であり、パーティション数が16であっても同様です。 - OceanBaseクラスタに3つのOBServerノードがある場合、各ノードが 1つのワーカースレッド(Worker Thread) を起動するため、合計スレッド数は3になります。
ヒントを使用したパーティションテーブルの並列度の指定
PARALLEL ヒントを使用すると、デフォルト値を上書きして DOP を強制的に指定できます。
例:明示的にDOP=8を設定する
上記のパーティションテーブル ptable に対して、PARALLEL ヒントを追加してパラレルクエリを開始し、dop値を指定します。そして、EXPLAIN コマンドを使用して生成された実行計画を確認します。
obclient> EXPLAIN SELECT /*+ PARALLEL(8) */ * FROM ptable;
実行計画の出力:
Query Plan:
=======================================================
|ID|OPERATOR |NAME |EST. ROWS|COST |
-------------------------------------------------------
|0 |EXCHANGE IN DISTR | |1600000 |1246946|
|1 | EXCHANGE OUT DISTR |:EX10000|1600000 |1095490|
|2 | PX PARTITION ITERATOR| |1600000 |1095490|
|3 | TABLE SCAN |ptable |1600000 |1095490|
=======================================================
Outputs & filters:
-------------------------------------
0 - output([ptable.c1], [ptable.c2]), filter(nil)
1 - output([ptable.c1], [ptable.c2]), filter(nil), dop=8
2 - output([ptable.c1], [ptable.c2]), filter(nil)
3 - output([ptable.c1], [ptable.c2]), filter(nil),
access([ptable.c1], [ptable.c2]), partitions(p[0-15])
ワーカースレッドの割り当てルール:
クエリパーティションが存在するOBServerノード ≤ DOP:この場合、ワーカースレッド(合計数はdop値と等しい)は一定のポリシーに従って関連するOBServerノードに割り当てられます。
- 例えば、dop=8の場合、16個のパーティションが4台のOBServerノードに均等に分散していると、各OBServerノードでは2つのワーカースレッドが起動され、それぞれのパーティションをスキャンします(合計8つのワーカースレッドが起動されます)。
クエリパーティションが存在するOBServerノード > DOP:各OBServerノードでは少なくとも1つのワーカースレッドが起動され、必要なワーカースレッドの総数はdop値よりも多くなります。
- 16個のパーティションが16台のOBServerノードに分散している場合(各ノードに1個のパーティション)、各OBServerノードでは1つのワーカースレッドが起動され、それぞれのパーティションをスキャンします(合計16個のワーカースレッドが起動されます)。
パーティションテーブルの単一パーティションクエリにおける強制的な並列処理
クエリの対象となるパーティション数が1であっても(例えばWHERE条件によるフィルタリング)、PARALLELヒントを使用してパーティション内の並列処理を有効にすることができます(ただし、DOP ≥2が必要です)。
例:単一パーティションに対する強制的な並列処理
EXPLAIN SELECT /*+ PARALLEL(8) */ * FROM ptable WHERE c1 = 1;
実行計画出力:
Query Plan:
=================================================
|ID|OPERATOR |NAME |EST. ROWS|COST |
-------------------------------------------------
|0 |EXCHANGE IN DISTR | |990 |85316|
|1 | EXCHANGE OUT DISTR|:EX10000|990 |85222|
|2 | PX BLOCK ITERATOR| |990 |85222|
|3 | TABLE SCAN |ptable |990 |85222|
=================================================
Outputs & filters:
-------------------------------------
0 - output([ptable.c1], [ptable.c2]), filter(nil)
1 - output([ptable.c1], [ptable.c2]), filter(nil), dop=8
2 - output([ptable.c1], [ptable.c2]), filter(nil)
3 - output([ptable.c1], [ptable.c2]), filter([ptable.c1 = 1]),
access([ptable.c1], [ptable.c2]), partitions(p1)
例の分析:
WHERE c1=1はクエリを1つのパーティションに限定します。デフォルトではDOP=1ですが、PARALLEL(8)を使用してDOP=8に強制的に設定します。- この場合、システムは単一パーティションに対してパーティション内並列処理を開始します(
DOP ≥2が必要です)。
注意
- パーティションテーブルの場合、
PARALLELヒントにdop ≥2を指定しない限り、パーティション内の並列処理は開始されません(パーティション数が1であっても同様です)。 - パーティションテーブル以外の場合、パーティションが1つしかないため、
PARALLELヒントでdop ≥2を指定しない限り、並列処理はトリガーされません。 dop値が空または2未満の場合、システムは並列処理の設定を無視します。
パーティション化されていないテーブルのパラレルクエリを有効にする
パーティション化されていないテーブルは本質的に 単一パーティションテーブル であるため、以下のような制限があります。
- 自動並列化不可:
PARALLELヒントを使用しても、DOP <2の場合は無効です。 - 強制的な並列化条件:
PARALLELヒントでDOP ≥2を指定する必要があります。
例:パーティション化されていないテーブルの強制的な並列化
パーティション化されていないテーブル stable を作成します:
CREATE TABLE stable(c1 INT, c2 INT);
stable に対してフルスキャンを実行し、EXPLAIN コマンドで生成された実行計画を確認します:
EXPLAIN SELECT * FROM stable;
実行計画出力:
Query Plan:
======================================
|ID|OPERATOR |NAME |EST. ROWS|COST |
--------------------------------------
|0 |TABLE SCAN|stable|100000 |68478|
======================================
Outputs & filters:
-------------------------------------
0 - output([stable.c1], [stable.c2]), filter(nil),
access([stable.c1], [stable.c2]), partitions(p0)
例の分析:ヒントを使用しない場合、パーティション化されていないテーブルではパラレルクエリは開始されません。
PARALLEL ヒントを追加してパーティション内のパラレルクエリを有効にし、dop値(2以上)を指定します。EXPLAIN コマンドで生成された実行計画を確認します。
EXPLAIN SELECT /*+ PARALLEL(4) */ * FROM stable;
実行計画出力:
Query Plan:
=================================================
|ID|OPERATOR |NAME |EST. ROWS|COST |
-------------------------------------------------
|0 |EXCHANGE IN DISTR | |100000 |77944|
|1 | EXCHANGE OUT DISTR|:EX10000|100000 |68478|
|2 | PX BLOCK ITERATOR| |100000 |68478|
|3 | TABLE SCAN |stable |100000 |68478|
=================================================
Outputs & filters:
-------------------------------------
0 - output([stable.c1], [stable.c2]), filter(nil)
1 - output([stable.c1], [stable.c2]), filter(nil), dop=4
2 - output([stable.c1], [stable.c2]), filter(nil)
3 - output([stable.c1], [stable.c2]), filter(nil),
access([stable.c1], [stable.c2]), partitions(p0)
例の分析:
- デフォルトでは
PARALLELヒントがない場合、stableのDOP=1となります(パーティション化されていないテーブルはデフォルトで並列化されません)。 PARALLEL(4)と明示的に設定すると、DOP=4となり、システムがパーティション内の並列処理を開始します。
複数テーブルパラレルクエリの有効化
デフォルト動作
複数テーブルのJOINクエリにおける並列度は、各テーブルのパーティション戦略によって個別に決定されます。
- すべてのテーブルのターゲットパーティション数が1以下の場合、デフォルトでは並列処理は有効になりません。
- パーティション数が1より大きいテーブルが存在する場合、自動的に並列処理が有効になります(デフォルトの
DOP=1)。
例:複数テーブルのデフォルト並列処理
-- 最初に、パーティションテーブルp1tableとp2tableを作成します
CREATE TABLE p1table(c1 INT, c2 INT)
PARTITION BY HASH(c1) PARTITIONS 2;
CREATE TABLE p2table(c1 INT, c2 INT)
PARTITION BY HASH(c1) PARTITIONS 4;
テーブル作成が完了したら、EXPLAIN を使用して p1table と p2table のJOIN結果を照会します。JOIN条件は p1table.c1=p2table.c2 です。
EXPLAIN SELECT * FROM p1table p1 JOIN p2table p2 ON p1.c1=p2.c2;
実行計画出力:
Query Plan:
====================================================================
|ID|OPERATOR |NAME |EST. ROWS|COST |
--------------------------------------------------------------------
|0 |EXCHANGE IN DISTR | |784080000|614282633|
|1 | EXCHANGE OUT DISTR |:EX10001|784080000|465840503|
|2 | HASH JOIN | |784080000|465840503|
|3 | EXCHANGE IN DISTR | |200000 |155887 |
|4 | EXCHANGE OUT DISTR (BROADCAST)|:EX10000|200000 |136955 |
|5 | PX PARTITION ITERATOR | |200000 |136955 |
|6 | TABLE SCAN |p1 |200000 |136955 |
|7 | PX PARTITION ITERATOR | |400000 |273873 |
|8 | TABLE SCAN |p2 |400000 |273873 |
====================================================================
Outputs & filters:
-------------------------------------
0 - output([p1.c1], [p1.c2], [p2.c1], [p2.c2]), filter(nil)
1 - output([p1.c1], [p1.c2], [p2.c1], [p2.c2]), filter(nil), dop=1
2 - output([p1.c1], [p1.c2], [p2.c1], [p2.c2]), filter(nil),
equal_conds([p1.c1 = p2.c2]), other_conds(nil)
3 - output([p1.c1], [p1.c2]), filter(nil)
4 - output([p1.c1], [p1.c2]), filter(nil), dop=1
5 - output([p1.c1], [p1.c2]), filter(nil)
6 - output([p1.c1], [p1.c2]), filter(nil),
access([p1.c1], [p1.c2]), partitions(p[0-1])
7 - output([p2.c1], [p2.c2]), filter(nil)
8 - output([p2.c1], [p2.c2]), filter(nil),
access([p2.c1], [p2.c2]), partitions(p[0-3])
例の分析:
p1table(2パーティション)とp2table(4パーティション)のデフォルトDOP=1であるため、合計DOP=1となります。
複数テーブルの並列度を明示的に設定する
PARALLEL ヒントを使用して、複数テーブルの JOIN に対してグローバルな DOP を指定できます。
例:グローバルDOP=8
obclient> EXPLAIN SELECT /*+ PARALLEL(8) */ *
FROM p1table p1 JOIN p2table p2 ON p1.c1=p2.c2;
この場合、p1table と p2table の両方の並列度は DOP=8 で実行されます。
特殊なシナリオ:パーティションフィルタリング後の並列処理
クエリ条件により、特定のテーブルのターゲットパーティション数が1以下に制限される場合:
- デフォルトではこのテーブルは並列処理されませんが、
PARALLELヒントを使用してパーティション内の並列処理を強制的に有効にできます(ただし、DOP ≥2が必要です)。
例:一部のパーティションのフィルタリング
EXPLAIN SELECT /*+ PARALLEL(8) */ * FROM p1table p1 JOIN p2table p2 ON p1.c1=p2.c2 AND p2.c1=1;
実行計画出力:
Query Plan:
=============================================================
|ID|OPERATOR |NAME |EST. ROWS|COST |
-------------------------------------------------------------
|0 |EXCHANGE IN DISTR | |1940598 |1807515|
|1 | EXCHANGE OUT DISTR |:EX10001|1940598 |1440121|
|...|...|...|...|
=============================================================
Outputs & filters:
-------------------------------------
1 - dop=8
例の分析:
p2tableはWHERE c1=1により1つのパーティションのみをスキャンするため、デフォルトでDOP=1となります。PARALLEL(8)ヒントを使用してグローバルなDOP=8を強制的に設定し、デフォルト値を上書きします。
パラレル実行の監視
OceanBaseは、システムビュー(G)V$OB_SQL_AUDITを提供しており、パラレル実行の実行状態や統計情報を確認できます。主なフィールドは以下のとおりです:
qc_id:パラレル実行シナリオにおけるスケジューラーIDdfo_id:パラレル実行シナリオにおける現在実行中のサブプランIDsqc_id:パラレル実行シナリオにおけるローカルコーディネーターIDworker_id:パラレル実行シナリオにおけるワーカースレッドID
詳細については、次のドキュメントを参照してください:(G)V$OB_SQL_AUDIT