これは、SQLServerの行モード並列プランの実行を開始する方法を深く掘り下げた5部構成のシリーズの第4部です。パート1は親タスクの実行コンテキストゼロを初期化し、パート2はクエリスキャンツリーを作成しました。パート3はクエリスキャンを開始し、初期段階を実行しました 処理し、ブランチCで最初の追加の並列タスクを開始しました。
これは実行シーケンスの2番目のステップです:
- ブランチA(親タスク)。
- ブランチC(追加の並列タスク)。
- ブランチD(追加の並列タスク)。
- ブランチB(追加の並列タスク)。
並行計画の支店のリマインダー(クリックして拡大)
新しいタスクの直後 ブランチCがキューに入れられている場合、SQLServerはワーカーを接続します 各タスクに割り当て、ワーカーをスケジューラーに配置します 実行の準備ができました。新しい各タスクは、新しい実行コンテキスト内で実行されます。 DOP 2には、2つの新しいタスク、2つのワーカースレッド、およびブランチCの2つの実行コンテキストがあります。各タスクは、ブランチCのイテレーターの独自のコピーを独自のワーカースレッドで実行します。
2つの新しい並列タスクは、サブプロシージャで実行を開始します 最初にOpen
につながるエントリポイント 取引所のプロデューサー側に電話します(CQScanXProducerNew::Open
)。どちらのタスクも、最初は同じコールスタックを持っています:
一方、親タスク (独自のワーカースレッドで実行)新しいサブプロセスをサブプロセスマネージャーに登録してから、待機 消費者側 ノード5での再パーティションストリーム交換の例。親タスクはCXPACKET
で待機します。 *すべてまで ブランチCの並列タスクのOpen
を完了します 呼び出して、取引所のプロデューサー側に戻ります。並列タスクは、サブツリー内のすべてのイテレータを開きます(つまり、インデックスシークまで) ノード9以降)ノード5での再パーティションストリーム交換に戻る前に、親タスクはCXPACKET
で待機します。 これが起こっている間。親タスクが初期段階の呼び出しを実行していることを忘れないでください。
この待機は、待機中のタスクDMVで確認できます:
実行コンテキスト0(親タスク)は、両方の新しい実行コンテキストによってブロックされます。これらの実行コンテキスト はコンテキストゼロの後に作成される最初の追加のものであるため、番号1と2が割り当てられます。強調するために:両方の新しい実行コンテキストは、サブツリーを開き、親タスクのCXPACKET
の交換に戻る必要があります 終了するのを待ちます。
CXCONSUMER
が表示されることを期待していた可能性があります ここで待機しますが、その待機は行データで待機するために予約されています 到着する。現在の待機は行ではありません —プロデューサー側がオープンするためのものです 、したがって、一般的なCXPACKET
を取得します *待ってください。
*AzureSQLデータベースとマネージドインスタンスは新しいCXSYNC_PORT
を使用します CXPACKET
の代わりに待つ ここでは、その改善はまだSQL Serverに反映されていません(2019 CU9の時点)。
クエリプロファイルで新しいタスクを確認できます DMV。新しいタスクのプロファイリング情報は、それらの実行コンテキストが親(実行コンテキストゼロ)から派生した(クローン化されてから更新された)ため、DMVに表示されます:
ブランチC(強調表示)の各イテレータに3つのエントリがあります。親タスク用に1つ(実行コンテキスト0)、新しい追加の並列タスクごとに1つ(コンテキスト1および2)。スレッドごとの推定行数に注意してください (パート1を参照)が到着しました。並列タスクについてのみ表示されます。最初と最後のアクティブ時間 並列タスクの場合、実行コンテキストが作成された時刻を表します。新しいタスクはどれも開いていません まだイテレータはありません。
再パーティションストリーム ノード5での交換には、DMV出力に1つのエントリしかありません。これは、関連付けられた非表示のプロファイラーがコンシューマーを監視しているためです。 交換の側。追加の並列タスクはプロデューサーにあります 交換の側。ノード5のコンシューマー側は最終的に 並行するタスクがありますが、まだその点に到達していません。
これは、息を止めて、現在すべてがどこにあるかを要約するのに良いポイントのようです。進むにつれて、これらの停止点がさらに増えるでしょう。
- 親タスク 消費者側にあります ノード5で交換される再パーティションストリームの 、
CXPACKET
を待っています 。初期段階の呼び出しを実行している最中です。ブランチにブロッキングソートが含まれているため、ブランチCの起動を一時停止しました。親タスクの待機は、両方の並列タスクがサブツリーのオープンを完了するまで続きます。 - 2つの新しい並列タスク プロデューサー側 ノード5交換のは、ブランチCでイテレータを開く準備ができています。
親タスクがCXPACKET
から解放されるまで、この並列実行プランのブランチCの外部にあるものは前進できません。 待つ。これまでに、ブランチC用に追加の並列ワーカーのセットを1つだけ作成したことを思い出してください。他のスレッドは親タスクだけであり、それはブロックされています。
2つの並行タスクは、プロデューサー側から始まります。 ノード5での再パーティションストリーム交換の例。それぞれに、独自のストリーム集約、ソート、およびインデックスシークを備えた個別の(シリアル)プランがあります。計算がソートに延期されるため、計算スカラーはランタイムプランに表示されません。
インデックスシークの各インスタンスは並列対応です 互いに素な行のセットを操作します。これらのセットは、親タスク(パート1で説明)によって以前に作成された親行セットからオンデマンドで生成されます。シークのいずれかのインスタンスが新しい行のサブ範囲を必要とする場合、他のワーカースレッドと同期するため、同時に新しいサブ範囲を割り当てるのは1つだけです。使用される同期オブジェクトも、親タスクによって以前に作成されました。タスクが新しいサブ範囲を取得するために親行セットへの排他的アクセスを待機するとき、タスクはCXROWSET_SYNC
で待機します 。
Open
のシーケンス ブランチCの各タスクの呼び出しは次のとおりです。
-
CQScanXProducerNew::Open
。取引所のプロデューサー側に先行するプロファイラーがないことに注意してください。これはクエリチューナーにとっては残念なことです。 -
CXTransLocal::Open
-
CXPort::Register
-
CXTransLocal::ActivateWorkers
-
CQScanProfileNew::Open
。ノード6の上のプロファイラー。 -
CQScanStreamAggregateNew::Open
(ノード6) -
CQScanProfileNew::Open
。ノード7の上のプロファイラー。 -
CQScanSortNew::Open
(ノード7)
並べ替えは完全にブロック演算子です 。 Open
中に入力全体を消費します 電話。ここで探索する興味深い内部の詳細はたくさんありますが、スペースが短いので、ハイライトのみを取り上げます:
並べ替え サブツリーを開き、子が提供できるすべての行を使用して、ソートテーブルを作成します。並べ替えが完了すると、並べ替えは出力モードに移行する準備が整い、制御を親に戻します。並べ替えは後でGetRow()
に応答します 呼び出し、毎回次のソートされた行を返します。ソート入力中のコールスタックの例は次のとおりです。
実行は、各ソートがその子のインデックスシークから利用可能なすべての(互いに素な範囲の)行を消費するまで続きます。 。次に、ソートはClose
を呼び出します インデックスシークで、親のストリームアグリゲートに制御を戻します 。ストリームアグリゲートはカウンターを初期化し、制御をプロデューサーに戻します。 ノード5での再パーティション交換の側。Open
のシーケンス これで、このブランチでの呼び出しが完了しました。
この時点でのプロファイリングDMVには、更新されたタイミング番号と終了時間が表示されます 並列インデックスシークの場合:
親タスクがコンシューマーを待機していることを思い出してください すべてのプロデューサーが開くノード5の側。同様の同期プロセスが、プロデューサーの並列タスク間で発生するようになりました。 同じ交換の側:
各プロデューサータスクは、CXTransLocal::Synchronize
を介して他のタスクと同期します 。プロデューサーはCXPort::Open
を呼び出します 、次にCXPACKET
を待ちます すべての消費者側 開く並列タスク。最初のブランチC並列タスクが取引所のプロデューサー側に戻って待機すると、待機中のタスクDMVは次のようになります。
親タスクのコンシューマー側の待機はまだあります。新しいCXPACKET
強調表示されているのは、すべてのコンシューマー側の並列タスクを待機している最初のプロデューサー側の並列タスクです。 交換ポートを開きます。
コンシューマー側の並列タスク(ブランチB)はまだ存在しないため、プロデューサータスクはブロックされている実行コンテキストに対してNULLを表示します。再パーティションストリーム交換のコンシューマ側で現在待機しているタスクは、EarlyPhases
を実行している親タスク(並列タスクではありません!)です。 コードなので、カウントされません。
秒のとき ブランチCの並列タスクは、Open
から取引所のプロデューサー側に戻ります。 呼び出し、すべてのプロデューサーが交換ポートを開いたので、親タスク 消費者側 交換のリリース そのCXPACKET
から 待ってください。
プロデューサー側のワーカーは、コンシューマー側の並列タスクが作成されるのを待ち続け、交換ポートを開きます。
この時点で:
- 合計3つがあります タスク:ブランチCに2つ、および親タスク。
- 両方のプロデューサー ノード5で取引所がオープンし、待機中
CXPACKET
で コンシューマー側の並列タスクを開きます。交換機構(行バッファーを含む)の多くはコンシューマー側で作成されているため、プロデューサーが行を配置する場所はまだありません。 - 並べ替え ブランチCでは、すべての入力を消費し、ソートされた出力を提供する準備ができています。
- インデックスシーク ブランチCで作業が完了し、閉鎖されました。
- 親タスク
CXPACKET
の待機から解放されました ノード5のコンシューマ側で再パーティションストリーム交換。 まだ ネストされたEarlyPhases
の実行 呼び出します。
これは、実行シーケンスの3番目のステップです:
- ブランチA(親タスク)。
- ブランチC(追加の並列タスク)。
- ブランチD(追加の並列タスク)。
- ブランチB(追加の並列タスク)。
CXPACKET
からリリース ノード5での再パーティションストリーム交換のコンシューマ側で待機します。親タスク 昇順 ブランチBクエリスキャンツリー。ネストされたEarlyPhases
から戻ります マージ結合の外側(上部)入力にあるさまざまなイテレータとプロファイラへの呼び出し。
前述のように、昇順 ツリーは、非表示のプロファイリングイテレータによって記録された経過時間とCPU時間を更新します。親タスクを使用してコードを実行しているため、これらの数値は実行コンテキストゼロに対して記録されます。これは、前回の記事「実行プランのオペレーターのタイミングについて」で言及した「スレッド0」のタイミング番号の最終的な情報源です。
マージ結合に戻ると、親タスクはEarlyPhases
を呼び出します マージ結合への内側(下部)入力のイテレータとプロファイラの場合。これらはノード10〜15 です (延期される14を除く):
親タスクの初期フェーズの呼び出しがノード15のインデックスシークに到達すると、ノード11の再パーティションストリーム交換に到達するまで、ツリーの昇順を再開します(プロファイリング時間を設定します)。
次に、マージ結合への外部(上部)入力で行ったのと同じように、プロデューサー側を開始します ノード11での交換の 、2つの新しい並列タスクを作成します 。
これにより、ブランチDが動き始めます(以下を参照)。ブランチDは、ブランチCについてすでに詳細に説明したとおりに実行されます。
ブランチDのタスクを開始した直後、親タスクは待機します CXPACKET
で 新しいプロデューサーが交換ポートを開くためのノード11:
新しいCXPACKET
待機が強調表示されます。報告されたノードIDは少し誤解を招く可能性があることに注意してください。親タスクは、実際にはノード2(ストリームの収集)ではなく、ノード11(再パーティションストリーム)のコンシューマー側で待機しています。これは初期段階の処理の癖です。
その間、ブランチCのプロデューサースレッドはCXPACKET
を待機し続けます ノード5の再パーティションストリーム交換のコンシューマ側が開くようにします。
親タスクがブランチDのプロデューサーを開始した直後、クエリプロファイル DMVは、新しい実行コンテキスト(3および4)を表示します:
2つの新しい並列タスク ブランチDの場合は、ブランチCの場合とまったく同じように進みます。ソートはすべての入力を消費し、ブランチDのタスクは交換に戻ります。これにより、親タスクがCXPACKET
から解放されます。 待つ。次に、ブランチDのワーカーはCXPACKET
を待ちます ノード11のプロデューサー側で、コンシューマー側の並列タスクを開きます。それらの並列ワーカー(ブランチB)はまだ存在していません。
この時点で待機中のタスクを以下に示します。
ブランチCとDの両方の並列タスクセットがCXPACKET
を待機しています 並列タスクのコンシューマーが開くために、再パーティションストリームでそれぞれノード5と11を交換します。 実行可能なタスクのみ 現在、クエリ全体で親タスクがあります 。
この時点でのクエリプロファイラーDMVを以下に示し、ブランチCとDの演算子を強調表示します。
まだ開始していない並行タスクはブランチBのみです。これまでのブランチBでのすべての作業は、初期段階でした。 親タスクによって実行される呼び出し 。
このシリーズの最後のパートでは、この特定の並列実行プランの残りの部分がどのように開始されるかを説明し、プランがどのように結果を返すかについて簡単に説明します。最後に、任意の複雑さの並列プランに適用されるより一般的な説明を示します。