85

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

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

  • データは実際には Web サービス経由で入ってきます。Web サービスは、テキスト ファイルをデータベース サーバー上の共有フォルダーに書き込みますBULK INSERTINSERTこのプロセスはもともと SQL Server 2000 に実装されていましたが、当時はサーバーで数百のステートメントをチャッキングする以外に方法がありませんでした。

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

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

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

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

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

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

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

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

何かを本番環境に投入する前に、明らかにこれをプロファイリングするつもりですが、すべての時間を費やす前に、最初に周りに尋ねるのは良い考えかもしれないと思いました.この目的で TVP を使用することについて発行する厳しい警告があるかどうかを確認してください.

では、SQL Server 2008 に慣れていて、これを試したり、少なくとも調査したりした人は、どう判断するでしょうか? たとえば、かなり頻繁に発生する数百から数千行の挿入の場合、TVP はマスタードをカットしますか? 一括挿入と比べてパフォーマンスに大きな違いはありますか?


更新: クエスチョン マークが 92% 減少しました。

(別名: テスト結果)

最終結果は、36 段階の展開プロセスのように感じられるものを経て、現在運用中です。両方のソリューションが広範囲にテストされました。

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

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

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

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

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

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

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

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

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

    また

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

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

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

このインポート プロセスは何千回も連続して実行されることに注意してください。そのため、すべてのマージを完了するのにかかった時間 (はい、時間) をカウントするだけで、平均時間を簡単に求めることができます。

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

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

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


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

言い換えれば、私たちはすでにプロセスについて十分な洞察を持っており、ステージング テーブルの安全性は必要ありません。最初にステージング テーブルを用意した唯一の理由は、他の方法で使用しなければならなかったすべてのINSERTandステートメントでスラッシングを回避することでした。UPDATE元のプロセスでは、ステージング データがステージング テーブルに存在するのはほんの一瞬でした。

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

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

4

4 に答える 4

10

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

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

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

于 2010-01-27T20:25:17.797 に答える
5

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

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

おそらく、SQL 2008 の新しいMERGE機能が役立つでしょうか?

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

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

于 2010-01-27T20:19:26.100 に答える
-2

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

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

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

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

于 2010-01-27T21:56:54.893 に答える