数週間前、Twitterの#SQLHelpハッシュタグでASYNC_NETWORK_IO待機タイプに対する実行計画の影響について興味深い質問があり、いくつかの異なる意見と多くの良い議論が生まれました。
https://twitter.com/shawndube/status/1225476846537650176
これに対する私の直接の答えは、エンジンがTDSをクライアントに送信する結果を持っているが、それらを送信するための接続に使用可能なTDSバッファーがない場合に、ASYNC_NETWORK_IO待機タイプが発生するため、誰かがこれの原因と結果を誤って解釈しているということです。の上。一般的に、これはクライアント側が結果を効率的に消費していないことを意味しますが、その後の議論に基づいて、実行プランが実際にASYNC_NETWORK_IO待機に大きな影響を与えるかどうかをテストするのに十分興味をそそられました。
要約すると、ASYNC_NETWORK_IOに焦点を合わせると、チューニングメトリックが誤りであるため、単独で待機します。クライアントが結果を可能な限り速く消費している場合でも、クエリの実行速度が速いほど、この待機タイプが蓄積される可能性が高くなります。 (一般的に待機のみに焦点を当てることについてのGregの最近の投稿も参照してください。)
テスト構成
このためのテストを実行するために、コミュニティの別のメンバーから電子メールで提供された例に基づいて非常に単純なテーブルが生成されました。これは、待機タイプの変更を示していますが、2つの間でまったく異なるクエリもありました。 2番目のテストで使用されている追加のテーブルを使用したテストで、結果をオフにするコメントがありました。これにより、この待機タイプの重要な部分が最初から削除されるため、計画の変更だけではありません。
注:これは誰に対しても否定的な発言ではないことを指摘したいと思います。提供された元の複製から得られたその後の議論とさらなるテストは非常に教育的であり、それはこの待機タイプを全体的に理解するためのさらなる研究につながりました。元の複製DIDは違いを示していますが、提起された元の質問の一部ではなかった追加の変更があります。
DROP TABLE IF EXISTS [DemoTable]; CREATE TABLE [DemoTable] ( ID INT PRIMARY KEY, FILLER VARCHAR(100) ); INSERT INTO [DemoTable] WITH (TABLOCK) SELECT TOP (250000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)), REPLICATE('Z', 50) FROM master..spt_values t1 CROSS JOIN master..spt_values t2 CROSS JOIN master..spt_values t3 OPTION (MAXDOP 1); GO
このテーブルをヒントを使用してさまざまな平面形状をテストするための基本データセットとして使用し、次のクエリを使用しました。
SELECT t1.ID, t2.FILLER, t2.FILLER FROM [DemoTable] t1 INNER HASH JOIN [DemoTable] t2 ON t1.ID = t2.ID; SELECT t1.ID, t2.FILLER, t2.FILLER FROM [DemoTable] t1 INNER MERGE JOIN [DemoTable] t2 ON t1.ID = t2.ID; SELECT t1.ID, t2.FILLER, t2.FILLER FROM [DemoTable] t1 INNER LOOP JOIN [DemoTable] t2 ON t1.ID = t2.ID;
これらのクエリをSQLServer2019 CU1で実行していたため、実行プランには、クエリの実行に関連する実際の待機統計情報が含まれていました。
注: オプティマイザーは、この特定のデータセットとクエリにヒントを適用せずにマージ結合を使用します。
初期テスト結果
最初のテストでは、SSMSを使用してクエリを実行し、実際の実行プランを収集して、以下に示す各クエリに関連付けられた待機情報を比較しました。このサイズのデータの場合、経過時間に大きな違いはなく、ASYNC_NETWORK_IOの待機時間や待機カウントにも違いはありません。
ハッシュ結合
<WaitStats> <Wait WaitType="CXPACKET" WaitTimeMs="18393" WaitCount="8415" /> <Wait WaitType="ASYNC_NETWORK_IO" WaitTimeMs="4394" WaitCount="6635" /> <Wait WaitType="HTDELETE" WaitTimeMs="957" WaitCount="6" /> <Wait WaitType="HTBUILD" WaitTimeMs="4" WaitCount="6" /> <Wait WaitType="HTREPARTITION" WaitTimeMs="3" WaitCount="6" /> <Wait WaitType="CMEMTHREAD" WaitTimeMs="3" WaitCount="14" /> <Wait WaitType="LATCH_EX" WaitTimeMs="2" WaitCount="8" /> </WaitStats> <QueryTimeStats CpuTime="1068" ElapsedTime="4961" />
MERGE JOIN
<WaitStats> <Wait WaitType="ASYNC_NETWORK_IO" WaitTimeMs="3169" WaitCount="6592" /> </WaitStats> <QueryTimeStats CpuTime="792" ElapsedTime="3933" />
LOOP JOIN
<WaitStats> <Wait WaitType="CXPACKET" WaitTimeMs="13690" WaitCount="8286" /> <Wait WaitType="ASYNC_NETWORK_IO" WaitTimeMs="3576" WaitCount="6631" /> <Wait WaitType="LATCH_EX" WaitTimeMs="1" WaitCount="3" /> </WaitStats> <QueryTimeStats CpuTime="2172" ElapsedTime="4084" />
ただし、私自身の経験から、ManagementStudioはSQLServerからの結果を非常に非効率的に利用しており、それ自体が原因である可能性があることが繰り返し示されているため、これはテストを中止したかった場所ではありませんでした。 ASYNC_NETWORK_IOは発生するのを待ちます。そこで、テストの方法を変更して、クエリのSQLCMD実行に進むことにしました。
SQLCMDを使用したテスト
プレゼンテーション中にSQLCMDを頻繁に使用するため、次の内容のtestscript.sqlファイルを作成しました。
PRINT 'Minimize Screen'; GO WAITFOR DELAY '00:00:05'; GO SELECT t1.ID, t2.FILLER, t2.FILLER FROM [DemoTable] t1 INNER HASH JOIN [DemoTable] t2 ON t1.ID = t2.ID; GO SELECT t1.ID, t2.FILLER, t2.FILLER FROM [DemoTable] t1 INNER MERGE JOIN [DemoTable] t2 ON t1.ID = t2.ID; GO SELECT t1.ID, t2.FILLER, t2.FILLER FROM [DemoTable] t1 INNER LOOP JOIN [DemoTable] t2 ON t1.ID = t2.ID; GOからのフィラー
これは次のようにコマンドラインから実行され、5秒間の遅延中にウィンドウが最小化され、処理中に結果がレンダリングおよびスクロールされないようになりました。
sqlcmd-S。\SQL2019-i testscript.sql -dAdventureWorks2017実際の実行プランをキャプチャするために、query_post_execution_showplanイベントを収集する拡張イベントセッションを使用しました。後から考えると、SQL Server 2019では、軽量のクエリ実行統計プロファイリングインフラストラクチャv3実装を使用する代わりに、query_post_execution_plan_profileを使用する必要があると思いましたが、このイベントquery_post_execution_showplanも同時に有効にしない限り、WaitStatsまたはQueryTimeStats情報を返しません。さらに、これは他のワークロードがない分離されたテストマシンであるため、標準プロファイリングの影響はここではそれほど大きな問題ではありません。
CREATE EVENT SESSION [Actual Plan] ON SERVER ADD EVENT sqlserver.query_post_execution_showplan (ACTION(sqlserver.session_id));
ハッシュ結合
<WaitStats> <Wait WaitType="CXPACKET" WaitTimeMs="45722" WaitCount="8674" /> <Wait WaitType="ASYNC_NETWORK_IO" WaitTimeMs="11321" WaitCount="6610" /> <Wait WaitType="HTDELETE" WaitTimeMs="1174" WaitCount="6" /> <Wait WaitType="HTREPARTITION" WaitTimeMs="4" WaitCount="6" /> <Wait WaitType="HTBUILD" WaitTimeMs="3" WaitCount="5" /> <Wait WaitType="LATCH_EX" WaitTimeMs="2" WaitCount="7" /> </WaitStats> <QueryTimeStats ElapsedTime="11874" CpuTime="1070" />
MERGE JOIN
<WaitStats> <Wait WaitType="ASYNC_NETWORK_IO" WaitTimeMs="10837" WaitCount="6602" /> </WaitStats> <QueryTimeStats ElapsedTime="11597" CpuTime="789" />
LOOP JOIN
<WaitStats> <Wait WaitType="CXPACKET" WaitTimeMs="43587" WaitCount="8620" /> <Wait WaitType="ASYNC_NETWORK_IO" WaitTimeMs="11177" WaitCount="6612" /> <Wait WaitType="LATCH_EX" WaitTimeMs="1" WaitCount="3" /> </WaitStats> <QueryTimeStats ElapsedTime="11696" CpuTime="2221" />
これは実際にはクエリを実行するためのより高速な方法ではなく、ウィンドウが最小化されて結果が目に見えてスクロールされない場合でも、コマンドラインユーティリティを使用してクエリを実行することでパフォーマンスが実際に低下しました。ウィンドウを開いた状態でのHASH実行時間は15708msで、ASYNC_NETWORK_IO待機時間は15126msでした。ただし、これは、同じ正確な結果に対して、結果を消費するクライアントのパフォーマンスがクエリの待機時間と実行時間の両方に影響を与えることを示しています。
並列処理の影響?
私が気づいたことの1つは、実行プランXMLにCXPACKETとLATCH_EXが存在することに基づいて、並列処理で実行されたプランは2つだけであるということでした。そのため、シリアル実行プランを強制すると、OPTION(MAXDOP 1)を使用したこれらの同じクエリの実行にどのような影響があるのでしょうか。
ハッシュ結合
<WaitStats> <Wait WaitType="ASYNC_NETWORK_IO" WaitTimeMs="4047" WaitCount="6379" /> </WaitStats> <QueryTimeStats CpuTime="602" ElapsedTime="4619" />
MERGE JOIN
<WaitStats> <Wait WaitType="ASYNC_NETWORK_IO" WaitTimeMs="3699" WaitCount="6608" /> </WaitStats> <QueryTimeStats CpuTime="810" ElapsedTime="4478" />
LOOP JOIN
<WaitStats> <Wait WaitType="ASYNC_NETWORK_IO" WaitTimeMs="2083" WaitCount="5385" /> </WaitStats> <QueryTimeStats CpuTime="1859" ElapsedTime="3918" />
ここで、全体的な待機回数が大幅に減少していないことに注意してください。シリアルループ参加プランのみが、それに関連する待機数または合計待機時間に大きな変更を加えます。これは、それがプラスのメリットであることを意味するわけではなく、クエリの実行時間は大幅に改善されませんでした。その特定のテストの結果に影響を与えた他の要因があるかもしれません。
次の表は、各テストのASYNC_NETWORK_IO待機時間とカウントをまとめたものです。
PlanType | 行 | WaitCount | WaitTime | ExecTime | AppName | MAXDOP 1 | パラレル |
---|---|---|---|---|---|---|---|
ハッシュ | 250,000 | 6,635 | 4,394 | 4,961 | SSMS | N | Y |
マージ | 250,000 | 6,592 | 3,169 | 3,933 | SSMS | N | N |
ループ | 250,000 | 6,631 | 3,576 | 4,084 | SSMS | N | Y |
ハッシュ | 250,000 | 6,610 | 11,321 | 11,874 | SQLCMD | N | Y |
マージ | 250,000 | 6,602 | 10,837 | 11,597 | SQLCMD | N | N |
ループ | 250,000 | 6,612 | 11,177 | 11,696 | SQLCMD | N | Y |
ハッシュ | 250,000 | 6,379 | 4,047 | 4,619 | SSMS | Y | N |
マージ | 250,000 | 6,608 | 3,699 | 4,479 | SSMS | Y | N |
ループ | 250,000 | 5,385 | 2,083 | 3,918 | SSMS | Y | N |
概要
この投稿の調査では、プランの変更やASYNC_NETWORK_IO待機タイプのすべての側面を網羅しているわけではありませんが、この待機は、クエリの実行に使用される実行プランの影響をあまり受けないことを示しています。サーバー全体の分析を実行する場合、この待機タイプをCXPACKET待機タイプとほぼ同じように分類します。ほとんどのワークロードで通常表示され、信じられないほど歪んでいて、ASYNC_NETWORK_IOを待機しているリードブロッカーでブロックするなど、クライアントによる結果の消費が遅いことを示す他のパフォーマンスの問題がない限り、通常の待機署名の一部として無視されるものワークロード」。