2008-08-23 13 views
52

SQL Server 2005データベースに大量のデータを挿入するC#クライアントでパフォーマンスのボトルネックを起こしています。プロセスをスピードアップします。SQL Server(C#クライアント)に多数のデータを一括して一括して挿入する最も速い方法は何ですか

私はすでに、SqlClient.SqlBulkCopy(TDSに基づいています)を使用して、データ転送を高速化し、多くの手助けをしましたが、まだもっと探しています。

私はこのようになり、簡単なテーブルがあります。私はContainerIDのとBinId各チャンクに一定であり、シーケンス値が0、nが約300行を平均チャンクにデータを挿入してい

CREATE TABLE [BulkData](
[ContainerId] [int] NOT NULL, 
[BinId] [smallint] NOT NULL, 
[Sequence] [smallint] NOT NULL, 
[ItemId] [int] NOT NULL, 
[Left] [smallint] NOT NULL, 
[Top] [smallint] NOT NULL, 
[Right] [smallint] NOT NULL, 
[Bottom] [smallint] NOT NULL, 
CONSTRAINT [PKBulkData] PRIMARY KEY CLUSTERED 
(
    [ContainerIdId] ASC, 
    [BinId] ASC, 
    [Sequence] ASC 
)) 

を値は主キーに基づいて事前ソートされます。

%Disk Timeパフォーマンスカウンタは100%で多くの時間を費やしているため、ディスクIOは主な問題ですが、私が得ているスピードはRAWファイルコピーよりも数桁小さいことは明らかです。

私があれば、それはどの助けるん:私は挿入をしていながら

  1. は、主キーを削除し、以降の
  2. DOは、同じスキーマに一時テーブルに挿入、それを再作成し、定期的にそれらを転送しますメインテーブルは、挿入が起こっているテーブルのサイズを小さく保つために小さい
  3. それ以外は何ですか?

- 私が得ているの応答に基づいて、私は少しを明確にしましょう:

ポートマン:私は、クラスタ化インデックスを使用していたデータがすべてインポートされたとき、私はデータにアクセスする必要がありますので、その順番で連続している。私は特に、データをインポートする際にインデックスがそこにある必要はありません。インポートのために制約を完全に削除するのではなく、挿入を実行している間にノンクラスタードPKインデックスを使用する利点はありますか?

Chopeen:データは他の多くのマシン上でリモートで生成されています(現在のSQLサーバーは現在約10個しか処理できませんが、これ以上追加できることが大好きです)。ローカルマシン上でプロセス全体を実行することは実用的ではありません。なぜなら、出力を生成するために50倍の入力データを処理する必要があるからです。

Jason:インポート処理中にテーブルに対して並行クエリを実行していないため、プライマリキーを削除して、それが役立つかどうかを確認します。

+0

http://msdn.microsoft.com/en-us/library/ms174335.aspx – JohnB

答えて

0

はい、あなたのアイデアが役に立ちます。
読み込み中に読み込みが行われない場合は、オプション1を使用してください。
処理中に宛先テーブルが照会されている場合は、オプション2を使用します。

@Andrew
質問。 300の塊であなたの挿入。あなたの挿入総額はいくらですか? SQLサーバーは、普通の300個の古いインサートを非常に高速に処理できるはずです。

0

可能であれば、サーバに割り当てられたメモリやサーバが使用するバッファサイズを増やすのはどうですか?

4

トランザクションを使ってみましたか?

あなたが書いていることから、サーバがディスクに100%の時間を費やしているとすると、アトミックなSQL文で各行のデータを送信しているため、サーバが1行ごとにコミット(ディスクに書き込む)する必要があります。

あなたの代わりにトランザクションを使用した場合、サーバは、トランザクションの終了時にのみたらをコミットします。

詳細については、どのような方法でサーバーにデータを挿入していますか? DataAdapterを使用してDataTableを更新するか、文字列を使用して各センテンスを実行しますか?

+0

今、これは良いことです。私は、クライアントアプリケーションから汎用のDbCommandコードを使用して挿入プロシージャを作成しています.SqlClient固有のものやSQL Serverのバルクツールを使用することはできません。この単純なヒントは、1分半から5秒間の実行時間を要しました。 – Whelkaholism

18

あなたは既にSqlBulkCopyを使用しています。これは適切なスタートです。

ただし、SqlBulkCopyクラスを使用しても、必ずしもSQLが一括コピーを実行するわけではありません。特に、SQL Serverが効率的な一括挿入を実行するには、いくつかの要件が満たされている必要があります。

さらに読書:好奇心のうち

、なぜあなたのインデックスには、そのように設定されていますか? ContainerId/BinId/Sequenceがのように多く、が非クラスタ化インデックスに適しているようです。このインデックスをクラスタ化する必要がある特別な理由はありますか?

1

SSIS packagesを使用してこれを行うことができるように思えます。それらはSQL 2000のDTSパッケージに似ています。私はプレーンテキストのCSVファイルから、既存のSQLテーブルから、さらには複数のワークシートにまたがる6桁の行を持つXLSファイルから、すべての変換に成功しました。 C#を使用してデータをインポート可能な形式(CSV、XLSなど)に変換した後、SQL ServerにスケジュールされたSSISジョブを実行させてデータをインポートさせることができます。

それはSSISパッケージを作成するために非常に簡単です、そこに内蔵されにSQL ServerのEnterprise Managerツール(「データのインポート」と表示されたと思う)ウィザードがだ、とウィザードの終了時に、それはあなたのようにそれを保存するオプションを提供しますSSISパッケージ一束の詳細情報on Technetもあります。

3

BCP - セットアップには苦労しますが、DBの夜明け以来周りにあり、非常に速いです。

この順序でデータを挿入しない限り、3部分インデックスは実際には遅くなります。後でそれを適用すると、実際には遅くなりますが、2番目のステップになります。

Sqlの複合キーは常に非常に遅く、キーが大きいほど遅くなります。

8

インデックスをノンクラスタードに変更した場合、劇的な改善が見られると思います。これには2つのオプションであなたを残します:

  1. 変更非クラスタ化へのインデックス、およびクラスタ化インデックス
  2. 変更非クラスタ化へのインデックスせずに、ヒープテーブルとしてそれを残したが、その後「IDのような(代理キーを追加します「)とが著しく遅く、あなたが読みせずにそれを

いずれかがあなたの挿入をスピードアップしますアイデンティティ、主キー、およびクラスタ化インデックスを作ります。

このように考えてみてください。今はSQLに一括挿入を指示していますが、追加するテーブルごとにSQL全体を並べ替えるようにSQLに依頼しています。ノンクラスタード・インデックスを使用すると、レコードが入った順序でレコードを追加し、次に希望の順序を示す別のインデックスを作成します。

3

私は本当に明るい人ではありません。SqlClient.SqlBulkCopyメソッドの経験はあまりありませんが、ここでは2セントの価値があります。私はそれがあなたと他の人々を助けてくれることを願っています(少なくとも、私の無知を呼び起こす原因になります)。

データベースのデータファイル(mdf)がトランザクションログファイル(ldf)とは別の物理ディスクにある場合を除いて、rawファイルのコピー速度は決して一致しません。さらに、クラスタ化されたインデックスは、より公平な比較のために、別の物理ディスク上にある必要があります。

未処理のコピーでは、索引付けのために選択フィールド(列)のソート順を記録したり、維持したりしていません。

ノンクラスタードIDシードの作成と既存のノンクラスタードインデックスのクラスタードインデックスへの変更については、Portmanに同意します。

クライアント(データアダプタ、データセット、データテーブルなど)でどのような構成を使用していますか。サーバー上のディスクioが100%の場合、クライアント構成の解析には、サーバーが現在処理できるよりも高速であるように見えるので、あなたの時間が最善ではないと思います。

あなたが最低限のロギングに関するポートマンのリンクをたどる場合は、私がもしあれば、トランザクションで一括コピーを周囲の多くを助けるとは思っていませんでしょうが、私は私の人生の中で間違って何度もしてきた;)

このウォン」あなたの現在の問題を把握しておけば、この次のコメントは次のボトルネック(ネットワークスループット)を助けてくれるかもしれません - 特にインターネットを介している場合 -

Chopeenも興味深い質問をしました。 300レコードのチャンクを挿入して挿入する方法はどうでしたか? SQL Serverにはデフォルトのパケットサイズ(4096バイトだと思います)があり、レコードのサイズを導出し、クライアントとサーバーの間で送信されるパケットを効率的に使用していることを確認してください。 (例えば、すべてのサーバ通信で明らかにそれを変更するサーバオプションとは対照的に、クライアントコードのパケットサイズを変更することができます。例えば、良い考えではありません。)たとえば、レコードサイズが300レコードバッチ2つ目のパケットをほとんど無駄にして2つのパケットを送信します。バッチレコード数が任意に割り当てられた場合は、簡単に簡単な数学を行うことが理にかなっています。

私が知ることができます(そしてデータ型のサイズについて覚えている)から、各レコード(int = 4バイトとsmallint = 2バイトの場合)は正確に20バイトです。 300レコードバッチを使用している場合は、300 x 20 = 6,000バイトを送信しようとしています(さらに、接続のオーバーヘッドを少し推測しています)。これらを200個のレコード・バッチ(200 x 20 = 4,000 +オーバーヘッド・ルーム)= 1パケットで送信するほうが効率的です。また、ボトルネックは依然としてサーバーのディスクioのように見えます。

私はあなたが同じハードウェア/構成でSqlBulkCopyに生のデータ転送を比較するが、ここでの課題は地雷だった場合、私も行くだろう場所ですしている実現:

この投稿をおそらくとしてもうあなたを助けにはなりませんそれはかなり古いですが、私はあなたのディスクのRAID構成が何で、どのディスクの速度を使用しているのか尋ねます。データファイルにRAID 5(理想的には1)を使用するRAID 10を使用するドライブにログファイルを配置してみてください。これは、ディスク上の異なるセクタへの多くのスピンドルの移動を減らし、非生産的な "移動"状態ではなく、より多くの時間の読み書きを可能にします。データとログファイルをすでに分離している場合は、データファイルとは異なる物理ディスクドライブ上にインデックスを作成します(クラスタードインデックスでのみ行うことができます)。これにより、ロギング情報をデータの挿入と同時に更新するだけでなく、インデックスの挿入(およびコストのかかるインデックスページ操作)を同時に実行できるようになります。ここで

18

では、SQL Serverでインデックスを有効/無効にすることができます方法は次のとおりです。ここで

--Disable Index ALTER INDEX [IX_Users_UserID] SalesDB.Users DISABLE 
GO 
--Enable Index ALTER INDEX [IX_Users_UserID] SalesDB.Users REBUILD

は、あなたが解決策を見つけるのに役立ついくつかのリソースです:

Some bulk loading speed comparisons

Use SqlBulkCopy to Quickly Load Data from your Client to SQL Server

Optimizing Bulk Copy Performance

確かNOCHECKとTABLOCKオプションに見て:

Table Hints (Transact-SQL)は非常に遅れたが、他の誰のために見つけること

INSERT (Transact-SQL)

+0

良い情報、ありがとう! – tbone

+0

このスレッドの情報は役に立ちましたか?http://dba.stackexchange.com/questions/30734/bulk-data-loading-and-transaction-log –

関連する問題