web-dev-qa-db-ja.com

SQL:CPUまたはIOでない場合、INSERTの速度が低下しているのは何ですか?

書き込みが多い製品のデータベースがあります。 SSDを備えた新しいサーバーマシンを購入しました。驚いたことに、挿入はストレージがはるかに遅い古いマシンよりも速くありませんでした。ベンチマーク中に、SQL Serverプロセスによって示されたIOレートが非常に低いことがわかりました。

たとえば、ループの周りにBEGIN TRANとCOMMITを追加した以外は、 このページ にあるスクリプトを実行しました。せいぜい、CPUが5%にほとんど触れていないのに、ディスク使用量が7Mb/sに達することがわかりました。サーバーには64Gbがインストールされており、10を使用しています。合計実行時間は、最初の呼び出しで2分15秒、その後の呼び出しで約1分でした。データベースは単純なリカバリ中で、テスト中はアイドル状態でした。各呼び出しの間にテーブルを削除しました。

なぜこのような単純なスクリプトがとても遅いのですか?ハードウェアはほとんど使用されていません。専用のディスクベンチマークツールとSQLIOはどちらも、読み取りと書き込みの両方でSSDが500Mb/s以上の速度で正しく動作することを示しています。ランダム書き込みはシーケンシャル書き込みよりも遅いことを理解していますが、クラスター化インデックスのないテーブルへのこのような単純な挿入ははるかに高速であると予想します。

結局のところ、シナリオははるかに複雑ですが、最初に単純なケースを理解する必要があると感じています。簡単に言うと、アプリケーションは古いデータを削除し、SqlBulkCopyを使用して新しいデータをステージングテーブルにコピーし、フィルタリングを実行し、最後にMERGEやINSERT INTOを使用して、データをファイナルテーブルにコピーします。

->編集1:Martin Smithによってリンクされた手順を実行したところ、次の結果が得られました。

[Wait Type]  [Wait Count] [Total Wait (ms)] [T. Resource Wait (ms)] [T. Signal Wait (ms)]
NETWORK_IO          5008              46735                 46587        148
LOGBUFFER           901               5994                  5977         17
PAGELATCH_UP        40                866                   865          1
SOS_SCHEDULER_YIELD 53279             219                   121          98
WRITELOG            5                 145                   145          0
PAGEIOLATCH_UP      4                 58                    58           0
LATCH_SH            5                 0                     0            0

表示する結果がないこと、SQLファイル以外に転送するデータがないことを考えると、変なNETWORK_IOがほとんどの時間を費やしていることがわかりました。 NETWORK_IOタイプにはすべてのIOが含まれていますか?

->編集2:20Gb RAMディスクを作成し、そこからデータベースをマウントしました。SSDでの最高の時間は48秒で、RAM =ディスクは37秒に低下しました。NETWORK_IOは依然として最大の待機です。RAMディスクへの最大書き込み速度は、1秒あたりマルチギガバイトを実行できましたが、約250Mb /秒でした。それでもCPUをあまり使用していなかったので、SQLを保持しているものは何ですか

20
Djof

私はそれが古い質問であることを知っていますが、これはまだ検索者を助けるかもしれず、それは時々ポップアップする問題です。

リソースのボトルネックを確認せずにパフォーマンスの上限に達している主な理由は、1つのセッションの単一スレッド内で処理できるものの制限に達したためです。ループは並列処理されませんが、すべての挿入は連続して行われます。

私の場合、300万行を挿入するのに36秒かかります。つまり、1行あたり36/30000000 = 0.000012秒です。それはかなり速いです。私のシステムでは、必要なすべてのステップを実行するには、0.000012が必要です。

それをより速く行う唯一の方法は、2番目のセッションを並行して開始することです。

2つのセッションを並行して開始すると、どちらも1500万回の挿入を実行します。どちらも18秒で終了します。さらにスケールアウトすることもできますが、現在のテストセットアップは2つの並列セッションで95%cpuに達しているため、3を実行すると、CPUボトルネックが発生するため、結果が歪んでしまいます。

2つの並列セッションを開始すると、両方とも300万行が挿入され、両方とも39秒で終了します。これで39秒で600万行になります。

それでも、NETWORK_IOの待機が表示されたままになります。

NETWORK_IO待機は、拡張イベントを使用してそれらを追跡しているという事実によって追加されます。私の場合、挿入には36秒かかります(平均)。 (最初のコメントの上記のリンクから)拡張イベント方法を使用する場合、これは登録されたものです:

Wait Type             Wait Count  Total Wait Time (ms) Total Resource Wait Time (ms) Total Signal Wait Time (ms)
NETWORK_IO            3455        68808                68802                         6
PAGEIOLATCH_SH        3           64                   64                            0
PAGEIOLATCH_UP        12          58                   58                            0
WRITE_COMPLETION      8           15                   15                            0
WRITELOG              3           9                    9                             0
PAGELATCH_UP          2           4                    4                             0
SOS_SCHEDULER_YIELD   32277       1                    0                             1
IO_COMPLETION         8           0                    0                             0
LATCH_SH              3           0                    0                             0
LOGBUFFER             1           0                    0                             0

68秒のNETWORK_IOが登録されていることがわかります。しかし、挿入ループは36秒かかった単一のスレッドアクションであるため、これは不可能です。 (はい、複数のスレッドが使用されますが、操作は並列ではなく逐次であるため、クエリの合計時間よりも長い待機時間を累積することはできません)

私が拡張イベントを使用せず、待機インスタンスのみが静止インスタンスでDMVを統計する場合(挿入を実行しているだけで)、次のようになります。

Wait Type                   Wait Count  Total Wait Time (ms)  Total Resource Wait Time (ms) Signal Resource Wait Time (ms)
SOS_SCHEDULER_YIELD             8873                 0.21                                    0.01                                    0.20
PAGEIOLATCH_UP                  3                    0.02                                    0.02                                    0.00
PREEMPTIVE_OS_AUTHENTICATIONOPS 17                   0.02                                    0.02                                    0.00
PAGEIOLATCH_SH                  1                    0.00                                    0.00                                    0.00

したがって、拡張イベントログに表示されていたNETWORK_IOは、挿入ループとは関係ありませんでした。 (nocountをオンにしないと、大規模な非同期ネットワークができますIO待機、+ 1マーティン)

ただし、拡張イベントトレースにNETWORK_IOが表示される理由はわかりません。イベントの非同期ファイルターゲットへの書き込みがASYNC_NETWORK_IOを蓄積することを確認してください。ただし、これは確実に、フィルタリングしているSPIDとは異なるSPIDですべて実行されます。私はこれを新しい質問として自分で尋ねるかもしれません)

10
Edward Dortland

通常、あなたは sys.dm_exec_requests 、特にwait_timewait_typeおよびwait_resource INSERTリクエスト用。これにより、INSERTをブロックしているものが明確に示されます。結果は、ロック競合、ファイル拡張イベント、ログフラッシュ待機、割り当て競合(PFSページラッチ競合としてマニフェスト)などなどであるかどうかを示します。測定したら、それに応じて質問を更新します。先に進む前に、ここで停止して Waits and Queues トラブルシューティング方法論を読むことを強くお勧めします。

9
Remus Rusanu

OPにリンクされているページで、BEGIN TRAN/COMMITをループの周りにテストスクリプトを実行しました。私のマシンでは、最初に完了するのに1:28かかりました。

次に、これらの2つのコマンドをループの外に移動しました。

_SELECT @Random = ROUND(((@Upper - @Lower -1) * Rand() + @Lower), 0)
SET @InsertDate = DATEADD(dd, @Random, GETDATE())
_

その後28秒で完了しました。

何が起こっているのかはよくわかりませんが、Rand()コードで何らかのスリープが発生している可能性があると思います。番号)。

FWIW、SSDは書き込みが多いアプリに最適なテクノロジーとは限りません。最高のパフォーマンスを得るには、DBログがDBデータとは別のドライブ文字にあり、ログファイルが最大サイズまで事前に成長していることを確認し、ログを切り捨てないでください。

3
RickNZ

速度低下を特定するために使用する別のDMVは sys.dm_os_waiting_tasks です。クエリがCPUを集中的に使用しない場合は、このDMVからの待機に関する詳細情報を見つけることができます。

1
StanleyJohns

SQL 2008の待機イベントのリストをチェックしていますが、NETWORK_IOが表示されません。 http://technet.Microsoft.com/en-us/library/ms179984(v = sql.100)。 aspx

私はNETWORK_IOがASYNC_NETWORK_IOとしてリストされているだけだと思っていたので、SQLのバージョンをもう一度確認できるかどうかを確認したいと思います。

ネットワーク待機がまったく表示されることについては、スタンドアロンサーバーで作業している場合でも発生する可能性があります。ネットワークカードの設定を確認しましたか?それらが問題かどうか疑問に思っています。

結局のところ、メモリ、CPU、ディスクI/O、ネットワーク、およびロックなど、考えられるリソースのボトルネックはごくわずかです。 CPUとI/Oが問題ではないこと、およびNETWORK_IOの待機イベントがあることを示したので、まずNIC=カードを確認することをお勧めします。

0
SQLRockstar