bcp / BULKINSERTとテーブル値パラメーターのパフォーマンス


83

BULK INSERTスキーマが変更されたため、SQL Serverのコマンドを使用してかなり古いコードを書き直さなければならないところです。代わりに、TVPを使用してストアドプロシージャに切り替えることを検討する必要があると思いましたが、どのような影響があるのでしょうか。パフォーマンスに影響する可能性があります。

私がこの質問をしている理由を説明するのに役立つかもしれないいくつかの背景情報:

  • データは実際にはWebサービスを介して受信されます。Webサービスは、データベースサーバー上の共有フォルダーにテキストファイルを書き込みます。共有フォルダーは、を実行しますBULK INSERT。このプロセスは元々SQLServer 2000に実装されていましたが、当時INSERTはサーバーで数百のステートメントをチャックする以外に方法はありませんでした。これは実際には元のプロセスであり、パフォーマンスの低下でした。

  • データは永続的なステージングテーブルに一括挿入されてから、はるかに大きなテーブルにマージされます(その後、ステージングテーブルから削除されます)。

  • 挿入するデータの量は「大規模」ですが、「巨大」ではありません。通常は数百行、まれに5〜1万行がトップになります。したがって、BULK INSERTログに記録されていない操作であってもそれほど大きな違いはないというの私の直感です(もちろん、私にはわかりません。したがって、質問です)。

  • 挿入は実際にははるかに大きなパイプラインバッチプロセスの一部であり、連続して何度も実行する必要があります。したがって、パフォーマンス重要です。

BULK INSERTTVPに置き換えたい理由は次のとおりです。

  • NetBIOSを介してテキストファイルを書き込むことは、おそらくすでにある程度のコストがかかり、アーキテクチャの観点からはかなり厄介です。

  • ステージングテーブルは削除できる(そして削除すべきである)と私は信じています。その主な理由は、挿入されたデータを挿入と同時に他のいくつかの更新に使用する必要があり、ほとんど空のステージングを使用するよりも、大規模な本番テーブルから更新を試みる方がはるかにコストがかかるためです。テーブル。TVPでは、パラメータは基本的にあるステージング表、私はメインの挿入後/前に私が欲しいものを行うことができます。

  • 重複チェック、クリーンアップコード、および一括挿入に関連するすべてのオーバーヘッドをほぼなくすことができました。

  • サーバーがこれらのトランザクションのいくつかを一度に取得する場合、ステージングテーブルまたはtempdbでのロックの競合について心配する必要はありません(回避しようとしますが、発生します)。

明らかに、本番環境に移行する前にこれをプロファイリングするつもりですが、この目的でTVPを使用することについて、誰かが厳しい警告を発するかどうかを確認するために、時間を費やす前にまず周りに尋ねるのは良い考えかもしれません。

つまり、SQL Server 2008に慣れていて、これを試したり、少なくとも調査したりした人にとって、評決は何でしょうか。たとえば、かなり頻繁に発生する数百から数千行の挿入の場合、TVPはマスタードをカットしますか?バルクインサートと比較してパフォーマンスに大きな違いはありますか?


更新:疑問符が92%少なくなりました!

(別名:テスト結果)

最終結果は、36段階の展開プロセスのように感じられた後、現在本番環境にあります。両方のソリューションが広範囲にテストされました。

  • 共有フォルダーコードをリッピングし、SqlBulkCopyクラスを直接使用します。
  • TVPを使用したスト​​アドプロシージャへの切り替え。

読者が正確にがテストされたかを知ることができるように、このデータの信頼性に関する疑問を和らげるために、このインポートプロセスが実際に行うことのより詳細な説明を以下に示します

  1. 通常は約20〜50データポイントの時間データシーケンスから始めます(ただし、数百になることもあります)。

  2. データベースからほとんど独立している、クレイジーな処理をたくさん実行します。このプロセスは並列化されているため、(1)のシーケンスの約8〜10が同時に処理されています。各並列プロセスは、3つの追加シーケンスを生成します。

  3. 3つのシーケンスすべてと元のシーケンスを取得し、それらを1つのバッチに結合します。

  4. 現在完了している8〜10個の処理タスクすべてのバッチを1つの大きなスーパーバッチに結合します。

  5. BULK INSERTストラテジー(次のステップを参照)またはTVPストラテジー(ステップ8にスキップ)のいずれかを使用してインポートします。

  6. SqlBulkCopyクラスを使用して、スーパーバッチ全体を4つの永続的なステージングテーブルにダンプします。

  7. JOIN(a)いくつかの条件を含む2つのテーブルで一連の集計ステップを実行し、(b)MERGE集計データと非集計データの両方を使用して6つの本番テーブルで実行するストアドプロシージャを実行します。(終了)

    または

  8. DataTableマージするデータを含む4つのオブジェクトを生成します。そのうちの3つにはCLRタイプが含まれていますが、残念ながらADO.NET TVPでは適切にサポートされていないため、文字列表現として組み込む必要があり、パフォーマンスが少し低下します。

  9. TVPをストアドプロシージャにフィードします。ストアドプロシージャは、基本的に(7)と同じ処理を実行しますが、受信したテーブルを直接使用します。(終了)

結果はかなり近いものでしたが、データが1000行をわずかに超えた場合でも、TVPアプローチは最終的に平均してパフォーマンスが向上しました。

このインポートプロセスは何千回も連続して実行されるため、すべてのマージを完了するのにかかった時間(はい、時間)を数えるだけで、平均時間を取得するのは非常に簡単でした。

元々、平均的なマージが完了するまでにほぼ正確に8秒かかりました(通常の負荷の下で)。NetBIOSクラッジを削除し、に切り替えるとSqlBulkCopy、時間がほぼ正確に7秒に短縮されました。TVPに切り替えると、バッチあたりの時間がさらに5.2秒に短縮されました。これは、実行時間が時間単位で測定されるプロセスのスループットが35%向上するため、悪くはありません。また、SqlBulkCopy。よりも約25%向上しています。

私は実際、真の改善はこれよりもはるかに大きいとかなり確信しています。テスト中に、最終的なマージはもはやクリティカルパスではないことが明らかになりました。代わりに、すべてのデータ処理を実行していたWebサービスは、着信する要求の数に応じて座屈し始めていました。CPUもデータベースI / Oも実際には限界に達しておらず、重要なロックアクティビティはありませんでした。場合によっては、連続するマージの間に数秒のアイドル秒のギャップが見られました。わずかなギャップがありましたが、を使用するとはるかに小さくなりました(0.5秒程度)SqlBulkCopy。しかし、それはまた別の日の話になると思います。

結論:テーブル値パラメーターはBULK INSERT、中規模のデータセットで動作する複雑なインポート+変換プロセスの操作よりも実際に優れたパフォーマンスを発揮します。


プロステージングテーブルである人々の一部の不安を和らげるために、もう1つポイントを追加したいと思います。ある意味で、このサービス全体は1つの巨大なステージングプロセスです。プロセスのすべてのステップは徹底的に監査されるため、特定のマージが失敗した理由を特定するためのステージングテーブルは必要ありません(実際にはほとんど発生しませんが)。サービスにデバッグフラグを設定するだけで、デバッガーにブレークするか、データベースではなくファイルにデータをダンプします。

言い換えれば、私たちはすでにプロセスについて十分な洞察を持っており、ステージングテーブルの安全性を必要としません。そもそもステージングテーブルを用意した唯一の理由は、他の方法で使用しなければならなかったであろうすべてのステートメントINSERTUPDATEステートメントをスラッシングしないようにするためでした。元のプロセスでは、ステージングデータはとにかくステージングテーブルに数分の1秒しか存在しなかったため、メンテナンス/メンテナンスの観点からは何の価値もありませんでした。

また、すべての操作をTVPに置き換えたわけではないことにも注意しBULK INSERTてください。大量のデータを処理する、および/またはデータをDBにスローする以外に特別なことを行う必要がないいくつかの操作では、引き続きが使用されますSqlBulkCopyTVPがパフォーマンスの万能薬であることを示唆しているのではなくSqlBulkCopy、最初のステージングと最後のマージの間のいくつかの変換を含むこの特定のインスタンスで成功したことだけを示しています。

だからあなたはそれを持っています。ポイントは、最も関連性の高いリンクを見つけるためにTToniに行きますが、他の回答にも感謝します。再度、感謝します!


これはそれ自体が驚くべき質問です。更新部分は答えにあるべきだと思います;)
Marc.23 7719年

回答:


10

私はまだTVPの経験がありませんが、MSDNのBULKINSERTと比較した優れたパフォーマンス比較チャートがここにあります

BULK INSERTの方が起動コストは高くなりますが、その後は高速になると言われています。リモートクライアントのシナリオでは、約1000行に線を引きます(「単純な」サーバーロジックの場合)。彼らの説明から判断すると、TVPを使用しても問題ないはずです。パフォーマンスへの影響(あるとしても)はおそらく無視できる程度であり、アーキテクチャ上の利点は非常に優れているようです。

編集:補足として、サーバーローカルファイルを回避し、SqlBulkCopyオブジェクトを使用してバルクコピーを使用することができます。DataTableにデータを入力し、それを「WriteToServer」(SqlBulkCopyインスタンスのメソッド)にフィードするだけです。使いやすく、非常に高速です。


リンクのおかげで、データが複雑なロジックをフィードするときにMSがTVPを推奨しているように見えるので、これは実際には非常に便利です(これはそうです)。また、バッチサイズをダイヤルアップまたはダイヤルダウンする機能もあるため、 1k列の問題点。これに基づいて、たとえそれが遅すぎたとしても、少なくとも試してみる価値があるかもしれません。
アーロノート2010年

ええ、リンクは面白いです。@ Aaronaught-このような状況では、潜在的なアプローチのパフォーマンスを調査して分析することは常に価値があるので、あなたの発見を聞いてみたいです!
AdaTheDev 2010年

7

@TToniの回答で提供されているリンクに関して言及されているチャートは、状況に応じて解釈する必要があります。私は、実際の研究は、(また、チャートのみで利用可能であるように思われることに注意して、これらの勧告に入ったどのくらいかわからない2008し、2008 R2そのドキュメントのバージョン)。

一方、SQL Serverカスタマーアドバイザリーチームからのこのホワイトペーパーがあります:TVPによるスループットの最大化

私は2009年からTVPを使用しており、少なくとも私の経験では、追加のロジックを必要としない宛先テーブルへの単純な挿入以外の場合(これはめったにありません)、通常はTVPの方が適していることがわかりました。

データ検証はアプリ層で行う必要があるため、ステージングテーブルは避ける傾向があります。TVPを使用することにより、これは簡単に対応でき、ストアドプロシージャのTVPテーブル変数は、その性質上、ローカライズされたステージングテーブルです(したがって、ステージングに実際のテーブルを使用する場合のように、同時に実行されている他のプロセスと競合することはありません。 )。

質問で行われたテストに関しては、最初に見つかったものよりもさらに高速であることが示される可能性があると思います。

  1. アプリケーションがTVPに値を送信する以外にDataTableを使用しない限り、DataTableを使用しないでください。IEnumerable<SqlDataRecord>コレクションをメモリ内で複製してDBに送信するだけではないため、インターフェイスの使用は高速で、使用するメモリも少なくなります。私はこれを次の場所に文書化しています:
  2. TVPはテーブル変数であるため、統計を維持しません。つまり、クエリオプティマイザに1行しかないことを報告します。したがって、procでは、次のいずれかを実行します。
    • 単純なSELECT以外の目的で、TVPを使用するクエリでは、ステートメントレベルの再コンパイルを使用します。 OPTION (RECOMPILE)
    • ローカル一時テーブル(つまり単一#)を作成し、TVPの内容を一時テーブルにコピーします

4

私はまだバルク挿入アプローチに固執すると思います。妥当な行数のTVPを使用すると、tempdbが引き続きヒットする場合があります。これは私の直感です。TVPを使用したパフォーマンスをテストしたとは言えません(他の人の入力も聞きたいですが)

.NETを使用するかどうかについては言及していませんが、以前のソリューションを最適化するために私が採用したアプローチは、SqlBulkCopyクラスを使用してデータの一括読み込みを行うことでした。以前にデータをファイルに書き込む必要はありません。ロードするには、SqlBulkCopyクラス(例)にDataTableを指定するだけです。これがDBにデータを挿入する最も速い方法です。5〜10K行はそれほど多くありませんが、これを最大75万行に使用しました。一般に、数百行の場合、TVPを使用しても大きな違いはないと思います。しかし、スケールアップは私見に制限されます。

おそらく、SQL 2008の新しいMERGE機能はあなたに利益をもたらすでしょうか?

また、既存のステージングテーブルが、このプロセスの各インスタンスに使用される単一のテーブルであり、競合などが心配な場合は、毎回新しい「一時的」で物理的なステージングテーブルを作成し、そのときに削除することを検討しましたか。で終わった?

インデックスなしでデータを入力することにより、このステージングテーブルへのロードを最適化できることに注意してください。次に、データが入力されたら、その時点で必要なインデックスを追加します(この時点では更新されないため、最適な読み取りパフォーマンスを得るためにFILLFACTOR = 100)。


私は.NETを使用していますが、プロセスは以前SqlBulkCopyからあり、変更されたことはありません。それについて私に思い出させてくれてありがとう、それは再訪する価値があるかもしれません。 MERGEもすでに広く使用されており、一時テーブルは以前に一度試されましたが、処理が遅く、管理が難しいことがわかりました。入力ありがとうございます!
アーロノート2010年

-2

ステージングテーブルは良いです!本当に私はそれを他の方法でやりたくありません。どうして?データのインポートが予期せず変更される可能性があるため(たとえば、列がまだ姓と名と呼ばれていても、姓の列に名のデータが含まれている場合など、予測できない方法で、例を選択しません)ランダムに。)ステージングテーブルの問題を簡単に調査できるため、インポートが処理した列にどのデータが含まれていたかを正確に確認できます。インメモリテーブルを使用すると、見つけにくいと思います。私と同じように生活のために輸入をしている人はたくさんいますが、全員がステージングテーブルの使用を勧めています。これには理由があるのではないかと思います。

小さなスキーマ変更を作業プロセスにさらに修正することは、プロセスを再設計するよりも簡単で時間もかかりません。それが機能していて、誰もそれを変更するために何時間も支払う気がない場合は、スキーマの変更のために修正する必要があるものだけを修正してください。プロセス全体を変更することにより、既存のテスト済みの作業プロセスに小さな変更を加えるよりも、はるかに多くの潜在的な新しいバグを導入できます。

そして、どのようにしてすべてのデータクリーンアップタスクを廃止するつもりですか?あなたはそれらを違ったやり方でやっているかもしれませんが、それでも彼らはやらなければなりません。繰り返しますが、あなたが説明する方法でプロセスを変更することは非常に危険です。

個人的には、新しいおもちゃで遊ぶ機会を得るのではなく、古いテクニックを使用することに腹を立てているように思えます。一括挿入が2000年である以外は、変更したいという本当の根拠がないようです。


27
SQL 2008は2年前から存在しており、このプロセスは何年も前から存在しており、変更を検討したのはこれが初めてです。最後の卑劣なコメントは本当に必要でしたか?
アーロノート2010年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.