3

コードにこのようなものがありました(.Net 2.0、MS SQL)

SqlConnection connection = new SqlConnection(@"Data Source=localhost;Initial
Catalog=DataBase;Integrated Security=True");
  connection.Open();

  SqlCommand cmdInsert = connection.CreateCommand();
  SqlTransaction sqlTran = connection.BeginTransaction();
  cmdInsert.Transaction = sqlTran;

  cmdInsert.CommandText =
     @"INSERT INTO MyDestinationTable" +
      "(Year, Month, Day, Hour,  ...) " +
      "VALUES " +
      "(@Year, @Month, @Day, @Hour, ...) ";

  cmdInsert.Parameters.Add("@Year", SqlDbType.SmallInt);
  cmdInsert.Parameters.Add("@Month", SqlDbType.TinyInt);
  cmdInsert.Parameters.Add("@Day", SqlDbType.TinyInt);
  // more fields here
  cmdInsert.Prepare();

  Stream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read);

  StreamReader reader = new StreamReader(stream);
  char[] delimeter = new char[] {' '};
  String[] records;
  while (!reader.EndOfStream)
  {
    records = reader.ReadLine().Split(delimeter, StringSplitOptions.None);

    cmdInsert.Parameters["@Year"].Value = Int32.Parse(records[0].Substring(0, 4));
    cmdInsert.Parameters["@Month"].Value = Int32.Parse(records[0].Substring(5, 2));
    cmdInsert.Parameters["@Day"].Value = Int32.Parse(records[0].Substring(8, 2));
    // more here complicated stuff here
    cmdInsert.ExecuteNonQuery()
  }
  sqlTran.Commit();
  connection.Close();

cmdInsert.ExecuteNonQuery ()をコメントアウトすると、このコードは 2 秒以内に実行されます。SQL 実行では 1 分 20 秒かかります。約 50 万件のレコードがあります。テーブルは前に空です。同様の機能の SSIS データ フロー タスクには、約 20 秒かかります。

  • 一括挿入オプションではありませんでした (以下を参照)。このインポート中に、私はいくつかの派手なことをしました。
  • 私のテスト マシンは、2 GB RAM を搭載した Core 2 Duo です。
  • タスク マネージャーを見ると、CPU が完全に使用されていませんでした。IOも十分に活用されていないようでした。
  • スキーマは地獄のように単純です: プライマリ インデックスとして AutoInt を持つ 1 つのテーブルと 10 個未満の int、tiny int、および chars(10)。

ここでいくつかの回答をした後、メモリから一括コピーを実行できることがわかりました! ファイルから実行する必要があると思っていたので、一括コピーの使用を拒否していました...

これを使用すると、約20秒かかります(SSISタスクのように)

  DataTable dataTable = new DataTable();

  dataTable.Columns.Add(new DataColumn("ixMyIndex", System.Type.GetType("System.Int32")));   
  dataTable.Columns.Add(new DataColumn("Year", System.Type.GetType("System.Int32")));   
  dataTable.Columns.Add(new DataColumn("Month", System.Type.GetType("System.Int32")));
  dataTable.Columns.Add(new DataColumn("Day", System.Type.GetType("System.Int32")));
 // ... and more to go

  DataRow dataRow;
  object[] objectRow = new object[dataTable.Columns.Count];

  Stream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read);

  StreamReader reader = new StreamReader(stream);
  char[] delimeter = new char[] { ' ' };
  String[] records;
  int recordCount = 0;
  while (!reader.EndOfStream)
  {
    records = reader.ReadLine().Split(delimeter, StringSplitOptions.None);

    dataRow = dataTable.NewRow();
    objectRow[0] = null; 
    objectRow[1] = Int32.Parse(records[0].Substring(0, 4));
    objectRow[2] = Int32.Parse(records[0].Substring(5, 2));
    objectRow[3] = Int32.Parse(records[0].Substring(8, 2));
    // my fancy stuf goes here

    dataRow.ItemArray = objectRow;         
    dataTable.Rows.Add(dataRow);

    recordCount++;
  }

  SqlBulkCopy bulkTask = new SqlBulkCopy(connection, SqlBulkCopyOptions.TableLock, null);
  bulkTask.DestinationTableName = "MyDestinationTable"; 
  bulkTask.BatchSize = dataTable.Rows.Count;
  bulkTask.WriteToServer(dataTable);
  bulkTask.Close();
4

12 に答える 12

9

各レコードを個別に挿入する代わりに、SqlBulkCopyクラスを使用してすべてのレコードを一度に一括挿入してみてください。

DataTable を作成し、すべてのレコードを DataTable に追加してから、SqlBulkCopyを使用します。WriteToServerを使用して、すべてのデータを一度に一括挿入します。

于 2008-09-24T14:43:56.123 に答える
3

取引は必要ですか?トランザクションを使用すると、単純なコマンドよりもはるかに多くのリソースが必要になります。

また、挿入された値が正しいことが確実な場合は、BulkInsert を使用できます。

于 2008-09-24T13:38:10.913 に答える
2

50 万件のレコードの場合、1 分はかなり合理的に聞こえます。これは 0.00012 秒ごとの記録です。

テーブルにインデックスはありますか? これらを削除して一括挿入後に再適用すると、オプションである場合、挿入のパフォーマンスが向上します。

于 2008-09-24T13:38:24.360 に答える
1

何らかの形式の一括挿入がオプションではない場合、別の方法として複数のスレッドがあり、それぞれがデータベースへの独自の接続を持っています。

現在のシステムの問題は、データベースへのラウンド トリップが 500,000 回あり、次のラウンド トリップを開始する前に最初のラウンド トリップが完了するのを待っていることです。あなたの時間は待っています。

なんらかの形式のプロデューサー/コンシューマー セットアップを使用して、ジョブを分割できれば、すべてのリソースをより多く利用できることに気付くかもしれません。

ただし、これを行うには、1 つの優れたトランザクションを失う必要があります。そうしないと、トランザクションが完了するまで、最初の書き込みスレッドが他のすべてのスレッドをブロックします。トランザクションは引き続き使用できますが、1 つの大きなトランザクションではなく、小さなトランザクションを多数使用する必要があります。

一括挿入メソッドを使用しているため、SSIS は高速になります。最初にすべての複雑な処理を実行し、挿入するデータの最終リストを生成して、同時に一括挿入します。

于 2008-09-24T14:54:54.163 に答える
1

1 秒あたり 8,333 レコードを処理することは、私には不合理ではないように思えます...どのようなスループットを期待していますか?

于 2008-09-24T13:40:29.147 に答える
1

より速い速度が必要な場合は、一括挿入の実装を検討してください。

http://msdn.microsoft.com/en-us/library/ms188365.aspx

于 2008-09-24T13:41:03.060 に答える
0

これは、bcpコマンドのようなものを使用して最もよく達成できます。それが利用できない場合は、BULKINSERTの使用に関する上記の提案が最善の策です。データベースへのラウンドトリップを500,000回行い、ログファイルに500,000エントリを書き込んでいます。もちろん、ログファイル、テーブル、およびインデックスに割り当てる必要のあるスペースもありません。

クラスタ化インデックスとは異なる順序で挿入する場合は、ディスク上の物理データを再編成するために必要な時間にも対処する必要があります。ここには多くの変数があり、クエリの実行が希望よりも遅くなる可能性があります。

コードからラウンドトリップする個々の挿入では、1秒あたり最大10,000トランザクションはひどいものではありません/

于 2008-09-24T14:19:00.473 に答える
0

BULK INSERT = アクセス許可からの bcp

INSERT をバッチ処理してラウンドトリップを減らすことができます SQLDataAdaptor.UpdateBatchSize = 10000 は 50 ラウンド トリップ

ただし、まだ500kの挿入があります...

論文

MSDN

于 2008-10-14T18:56:10.980 に答える
0

最初にすべてのレコードに対して、データに対して手の込んだ作業を行います。次に、一括挿入します。

(挿入後に選択を行っていないため.. BulkInsertの前にデータにすべての操作を適用する問題は見られません

于 2008-09-24T13:53:33.233 に答える
0

推測する必要があるとすれば、最初に探すのは、tbTrafficLogTTL テーブルのインデックスが多すぎるか、または間違った種類であるということです。テーブルのスキーマ定義を見ないと何とも言えませんが、次の場合に同様のパフォーマンスの問題が発生しました。

  1. 主キーは GUID で、主インデックスは CLUSTERED です。
  2. 一連のフィールドには、ある種の UNIQUE インデックスがあります。
  3. テーブルのインデックスが多すぎます。

50 万行のデータのインデックス作成を開始すると、インデックスの作成と維持にかかる時間が増えます。

また、年、月、日、時、分、秒のフィールドを単一の datetime2 またはタイムスタンプ フィールドに変換するオプションがある場合は、そうする必要があることにも注意してください。データ アーキテクチャに多くの複雑さを加えていますが、何のメリットもありません。このような分割フィールド構造を使用することを検討する唯一の理由は、何らかの理由で変更できない既存のデータベース スキーマを扱っている場合です。その場合、それはあなたであることを吸う。

于 2008-09-24T13:53:54.590 に答える
0

前回の契約でも同様の問題がありました。データを挿入するために SQL に 500,000 回アクセスしています。パフォーマンスを大幅に向上させるために、SQL 名前空間の BulkInsert メソッドを調査する必要があります。一括インポートを実装すると、数十のテーブルを復元するのに 2 時間以上かかっていた「リロード」プロセスが 31 秒に短縮されました。

于 2008-09-24T13:58:46.933 に答える
0

約 58 秒かかっているのは 500,000 レコードの物理的な挿入であると想定しています。データベース サーバー マシンの仕様を知らなければ (ローカルホストを使用しているようなので、ネットワークの遅延は問題にならないはずです)、これが良いか悪いか、ひどいものかを判断するのは困難です。

私はあなたのデータベーススキーマを見ていきます - 各挿入後に更新しなければならないインデックスがテーブルにたくさんありますか? これは、作業中のテーブルを参照する外部キーを持つ他のテーブルからのものである可能性があります。SQL Server には SQL プロファイリング ツールとパフォーマンス監視機能が組み込まれていますが、私はそれらを使用したことがありません。しかし、ロックなどの問題が発生する可能性があります。

于 2008-09-24T13:41:21.420 に答える