特定のシナリオでは、スレッドリソースを待機するため、パラレルクエリがキューイングされる可能性があります。
並行制御
テナントレベル変数 PARALLEL_SERVERS_TARGET を使用することで、各ノード上でテナントが提供できるパラレル実行ワーカースレッドの最大数を指定できます。パラレルクエリ開始前に、関連するすべてのobserverからワーカースレッドリソースを予約します。いずれか1つのobserverがパラレルクエリに十分なリソースを提供できない場合、そのクエリは実行されません。このようなパラレルクエリは再キューされ、次回の実行時にスレッドリソースの取得を再試行し、十分なワーカースレッドリソースを確保できるまで続けます。クエリの実行が完了すると、予約されたワーカースレッドリソースは直ちに解放されます。
この「ワーカースレッドリソースの取得を試行する - リソース不足で再キュー - 実行機会を再度得る - 再度ワーカースレッドリソースの取得を試行する」というプロセスを、パラレルクエリのキューイングと呼びます。パラレル実行リソースマネージャーは、すべてのobserverのワーカースレッドリソース予約を管理するモジュールです。
各パラレルクエリが必要とするワーカースレッド数を計算するために、パラレル実行リソースマネージャーはクエリプランをDFO分割し、DFOプロセスのスケジューリングをシミュレートし、parallel hintやtable parallelなどのパラメータに基づいて、そのクエリが各observer上で必要とする最大スレッド数を計算します。この一連のスレッド数を「リソースベクトル」と呼びます。
リソースベクトルは論理的な概念であり、並行性とキューイングを制御するために使用されます。リソースベクトルを通じてパラレル実行リソースマネージャーから十分なワーカースレッドリソースを予約した後、そのパラレルクエリは実行されます。実行中、異なるDFOのスケジューリングと実行に伴い、物理スレッドの取得と解放が繰り返されますが、論理的なスレッドリソースはパラレル実行リソースマネージャーに返却されません。パラレルクエリが完全に実行終了した後に、この一連のリソースベクトルがパラレル実行リソースマネージャーに返却されます。
多数のパラレルクエリがパラレル実行リソースマネージャーからスレッドリソースを予約する際、リソースが枯渇し、いずれのクエリのリソース要件も満たせなくなるまで、先着順のポリシーが適用されます。その後のクエリはすべて再キューされ、次回のスケジューリング時にリソース取得を再試行します。
ワーカースレッドの割り当て
テナントの各observer上には、パラレルクエリタスクを実行するためのパラレル実行スレッドプールがあります。タスク実行時にスレッドプール内のスレッド数が不足している場合、スレッドプールは動的に拡張されます。スレッドプール内のスレッドのアイドル時間が10分を超えると、自動的に10個のスレッドへのスケールインがトリガーされます。スレッドのアイドル時間が60分を超えると、さらなるスケールインがトリガーされ、スレッド数が0になる可能性があります。
一度パラレルクエリがスケジューリングされ実行されると、各DFOは常に、関与するobserverのパラレル実行スレッドプールから必要なパラレルスレッドリソースを取得できます。ただし、デフォルトでは、各DFOが1つのobserver上に割り当てるスレッド数は、テナントのMIN CPU × 10を超えてはなりません。DFOが要求するリソースがこの値を超える場合、自動的にMIN CPU × 10に引き下げられます。
二段階のリソース制御
任意のパラレルクエリは、二段階のリソース制御を経験します:
- グローバル制御:実行リソースマネージャーの制御の下、実行に十分なスレッドを含むリソースベクトルを予約します。
- ローカル制御:パラレル実行スレッドプールの制御の下、望ましい物理スレッド数を割り当てます。
グローバル制御は分散シナリオでのリソース取得を考慮し、ローカル制御は単一マシンのスレッドプールでのリソース割り当てのみを考慮します。両者はそれぞれの役割を果たします。前者は、Queryがチェックを通過した後は必ず実行され、実行時にリソースを取得できない問題に直面しないことを保証します。後者は、極端な状況下で単一QueryのDFOが効果的に利用可能な物理スレッド数を大幅に上回る数のスレッドを申請し、スレッドリソースの無駄遣いを防ぐことを保証します。パラレルクエリがグローバル制御段階を通過すれば、どの程度並行しても物理スレッド数が不足する問題に直面することなく、スムーズに実行されます。
パラレル実行リソース管理
パラレル実行リソースマネージャーはグローバルな視点を持ち、ビューGV$OB_PX_TARGET_MONITORを使用して、テナント内の各observerのスレッド予約状態を確認できます。ビューフィールドの詳細な意味については、GV$OB_PX_TARGET_MONITORを参照してください。
obclient> SELECT * FROM GV$OB_PX_TARGET_MONITOR;
+--------------+----------+-----------+-----------+-----------------+--------------+-----------+-------------+------------------+-------------------+------------------------------+
| SVR_IP | SVR_PORT | TENANT_ID | IS_LEADER | VERSION | PEER_IP | PEER_PORT | PEER_TARGET | PEER_TARGET_USED | LOCAL_TARGET_USED | LOCAL_PARALLEL_SESSION_COUNT |
+--------------+----------+-----------+-----------+-----------------+--------------+-----------+-------------+------------------+-------------------+------------------------------+
| 192.xx.xx.xx | 19512 | 1004 | N | 555393108309134 | 192.xx.xx.xx | 19510 | 10 | 6 | 0 | 0 |
| 192.xx.xx.xx | 19512 | 1004 | N | 555393108309134 | 192.xx.xx.xx | 19512 | 10 | 0 | 0 | 0 |
| 192.xx.xx.xx | 19510 | 1004 | Y | 555393108309134 | 192.xx.xx.xx | 19510 | 10 |
6 | 6 | 1 |
| 192.xx.xx.xx | 19510 | 1004 | Y | 555393108309134 | 192.xx.xx.xx | 19512 | 10 | 0 | 0 | 1 |
+--------------+----------+-----------+-----------+-----------------+--------------+-----------+-------------+------------------+-------------------+------------------------------+
4 rows in set
瞬間的には、異なるobserverが見るグローバル状態が一致しない場合がありますが、バックグラウンドで500ミリ秒ごとにグローバル状態が同期されます。全体として、各observerが見る状態は基本的に一致し、大きな偏差は生じません。