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


19

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

たとえば、ループの周りにBEGIN TRANとCOMMITを追加したことを除いて、このページにあるスクリプトを実行しました。せいぜい、ディスク使用量が7Mb / sに達するのを見ることができましたが、CPUはほとんど5%に触れませんでした。サーバーには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ディスクへの最大書き込み速度は約250Mb / sでしたが、1秒あたり数ギガバイトを実行できました。それはまだ多くのCPUを使用していなかったので、SQLを妨げているのは何ですか?



3
これNETWORK_IOは、300万件の「影響を受けた1行」メッセージが返送されている可能性があります。SET NOCOUNT ONスクリプトに追加してみましたか?
マーティンスミス

はい、NOCOUNTを追加しました。
Djof

2
奇妙な。そのとき、ネットワークアクティビティの方法にはあまり期待していません。実行の間に古い拡張イベントファイルを削除しましたか?それらを読み取るスクリプトはワイルドカードを使用しているEE_WaitStats*.xelため、古いカードは結果を汚染します。
マーティンスミス

よろしくお願いします。結果を明日更新します。
Djof

回答:


9

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

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

私の場合、300万行を挿入するのに36秒かかります。つまり、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で実行されます。私はこれを新しい質問として自分で尋ねるかもしれません)


1
「リソースのボトルネックを確認せずにパフォーマンスの上限に達しているのは、1つのセッションの単一スレッド内で処理できる限界に達したためです」:CPUの100%のボトルネック(1つのコア)について説明しています。ボトルネックがなければ、システム高速になるので、何か他のものが必要です。
レムスルサヌ

あなたの答えは非常に有益なエドワードです。データベースレイアウトの変更が必要ですが、並列処理は既に取り組んでいる問題の解決策のように見えます。しかし、Remusのように、マシンが(1つのうちの)すべてのCPUまたはディスクリソースを使用していないように思われる理由は今でも変わりません。
ジョフ

9

通常は、見てから始めるsys.dm_exec_requests、具体的に、wait_timewait_typewait_resource自分のINSERTリクエスト(S)のために。これにより、INSERTをブロックしているものが明確に示されます。結果は、ロック競合、ファイル成長イベント、ログフラッシュ待機、割り当て競合(PFSページラッチ競合としてのマニフェスト)などなどを示します。測定したら、それに応じて質問を更新します。先に進む前に、今すぐ停止して、待機とキューのトラブルシューティング方法を読むことを強くお勧めします。


3

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

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

SELECT @Random = ROUND(((@Upper - @Lower -1) * RAND() + @Lower), 0)
SET @InsertDate = DATEADD(dd, @Random, GETDATE())

それから28秒で完了しました。

何が起きているのかは確かではありませんが、 RAND()、おそらくエントロピー(より良い乱数)を生成するために使用しているアルゴリズムの一部としてコードています。

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


ご意見をお寄せいただきありがとうございます。RickNZ。コードをループ外に移動しても結果が速くなりませんでした。私が観察したのは、複数回実行すると速くなるということです。それはあなたが経験したことかもしれません。SSDは特効薬ではないことは知っていますが、それでもパフォーマンスは本来の性能ではないように感じます。
-Djof


0

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

NETWORK_IOは現在ASYNC_NETWORK_IOとしてリストされていると思ったので、SQLのバージョンを再度確認できるかどうかを尋ねたいと思いました。

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

1日の終わりには、メモリ、CPU、ディスクI / O、ネットワーク、ロックなど、リソースのボトルネックはわずかしかありません。CPUとI / Oは問題ではなく、NETWORK_IOの待機イベントがあることを示しているため、最初にこれらのNICカードを確認することをお勧めします。


1
NETWORK_IOOPは、拡張イベントを使用しているため、示されています。それはで更新やったことがなかったsys.dm_xe_map_values
マーティン・スミス

私は同じSQLRockstarを考えています。ネットワークカードを完全に無効にしようとしました。マーティンは、いくつかの古いファイルがまだ残っている可能性があることを指摘しました。私は明日結果を更新して、何か変更があるかどうかを確認します。
Djof

また、ステートメントの実行計画を確認できれば役立つかもしれません。
SQLRockstar
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.