Frage

Ich hatte sowas wie dies in meinem Code (.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();

Mit cmdInsert.ExecuteNonQuery () auf Kommentar dieser Code ausgeführt in weniger als 2 Sekunden. Mit SQL-Ausführung dauert es 1 m 20 s. Es gibt rund 0,5 Milionen Aufzeichnungen. Tabelle vor entleert. SSIS Datenflußtask ähnlicher Funktionalität dauert etwa 20 Sekunden.

  • Bulk Insert war ist keine Option (siehe unten). Ich habe einige fancy stuff während dieser Import.
  • Meine Testmaschine ist Core 2 Duo mit 2 GB RAM.
  • Wenn in Task-Manager CPU Suche war nicht voll untilized. IO schien auch nicht voll genutzt werden.
  • Schema ist einfach höllisch. Eine Tabelle mit AutoInt als Primärindex und weniger als 10 ints, winzige ints und Zeichen (10)

Nach einigen Antworten hier fand ich, dass es möglich ist, Bulk-Kopie aus dem Speicher ausführen ! Ich weigere Massenkopie zu verwenden beacuse Ich dachte, es aus der Datei getan werden muss ...

Jetzt benutze ich diese und es dauert aroud 20 s (wie SSIS-Task)

  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();
War es hilfreich?

Lösung

Statt jeden Datensatz einzeln einzufügen, führen Sie die SqlBulkCopy Klasse Bulk einfügen alle Datensätze auf einmal.

eine Datatable erstellen und fügen Sie alle Ihre Datensätze in die Datentabelle und dann a href verwenden <= „http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlbulkcopy.aspx“ rel = "noreferrer"> SqlBulkCopy . WriteToServer Masseneinfügung alle Daten auf einmal.

Andere Tipps

Ist die Transaktion erforderlich? Mit der Transaktion braucht viel mehr Ressourcen als einfache Befehle.

Auch wenn Sie sicher sind, dass die eingefügten Werte sind corect, können Sie eine BulkInsert verwenden.

1 Minute klingt ziemlich vernünftig 0,5 Millionen Datensätze. Das ist ein Rekord alle 0,00012 Sekunden.

Hat die Tabelle keine Indizes? Das Entfernen dieser und sie nach dem Masseneinsatz erneute Anwendung würde die Leistung zu verbessern der Einsätze, wenn das eine Option ist.

Es scheint mir nicht unvernünftig 8333 Datensätze zu verarbeiten pro Sekunde ... welche Art von Durchsatz erwarten Sie?

Wenn Sie eine bessere Geschwindigkeit benötigen, können Sie Masseneinfügung betrachten Umsetzung:

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

Wenn irgendeine Form von Masseneinsatz nicht möglich ist, würde die andere Art und Weise mehr Threads, die jeweils mit ihrer eigenen Verbindung zur Datenbank.

Das Problem mit dem aktuellen System ist, dass Sie 500.000 Umläufe auf die Datenbank haben, und für die erste Rundfahrt warten, um füllen Sie die nächste vor dem Start - jede Art von Latenz (dh ein Netzwerk zwischen den Maschinen) bedeutet, dass die meiste Zeit verbringen wir warten.

Wenn Sie den Job bis aufspalten können, vielleicht irgendeine Form von Producer / Consumer-Setup verwenden, könnten Sie feststellen, dass Sie viel mehr Nutzung aller Ressourcen erhalten.

Doch um dies zu tun, werden Sie die eine große Transaktion verlieren müssen - sonst der erste Schriftsteller Thread alle anderen blockiert werden, bis seine Transaktion abgeschlossen ist. Sie können immer noch Transaktionen verwenden, aber Sie werden eine Menge von kleinen anstatt 1 großen verwenden.

Der SSIS wird schnell sein, weil es die bulk-insert-Methode ist mit -. Alles tun, der komplizierten Verarbeitung zuerst, erzeugt die endgültige Liste der Daten, die sie alle zur gleichen Zeit zu bulk-insert einfügen und gibt

Ich gehe davon aus, dass, was die etwa 58 Sekunden nimmt die physische Einfügen von 500.000 Datensätzen ist - so sind Sie immer rund 10.000 Einsätze pro Sekunde. Ohne zu wissen, die Spezifikationen der Datenbank-Server-Maschine (ich sehe Sie verwenden localhost, so Verzögerungen im Netzwerk sollen kein Problem sein), ist es schwer zu sagen, ob dies gut, schlecht oder miserabel.

würde ich an Ihrer Datenbank-Schema finden - gibt es eine Reihe von Indizes auf die Tabelle, die nach jeder einfügen aktualisiert werden müssen? Dies könnte aus anderen Tabellen mit Fremdschlüsseln, die Tabelle verweisen Sie arbeiten. Es gibt SQL Profilierwerkzeuge und Performance-Monitoring-Einrichtungen in SQL Server integriert, aber ich habe sie nie benutzt. Aber sie können Probleme wie Schlösser auftauchen, und solche Dinge.

Haben die fancy stuff auf den Daten, auf alle Datensätze zuerst. Dann Massen einlegen.

(da Sie nicht wählen nach einem Einsatz zu tun .. ich sehe das Problem nicht alle Vorgänge der Anwendung auf den Daten vor dem BulkInsert

Wenn ich raten müsste, das erste, was ich für zu viele aussehen würde oder die falsche Art von Indizes für die tbTrafficLogTTL Tabelle. Ohne Blick auf die Schema-Definition für die Tabelle, kann ich nicht wirklich sagen, aber ich habe ähnliche Performance-Probleme erlebt, wenn:

  1. Der Primärschlüssel ist eine GUID und der Primärindex geclustert.
  2. Es gibt eine Art von UNIQUE-Index für eine Reihe von Feldern.
  3. Es gibt zu viele Indizes für die Tabelle.

Wenn Sie eine halbe Million Datenzeilen beginnen Indizierung, verbrachte die Zeit, Indizes zu erstellen und zu pflegen aufaddiert.

Ich werde auch beachten, dass, wenn Sie irgendeine Möglichkeit das Jahr, Monat, Tag, Stunde, Minute, Sekunde Felder in einem einzigen datetime2 oder Zeitstempel-Feld zu konvertieren, sollten Sie. Du fügt viel Komplexität zu Ihrer Datenarchitektur, für keinen Gewinn. Der einzige Grund, warum ich würde sogar eine Split-Feldstruktur wie die in Erwägung ziehen mit, wenn Sie mit einem bereits vorhandenen Datenbank-Schema zu tun hat, die aus irgendeinem Grunde nicht geändert werden können. In diesem Fall saugt es Ihnen zu sein.

Ich hatte ein ähnliches Problem in meinem letzten Vertrag. Sie machen 500.000 Fahrten zu SQL Ihre Daten einzufügen. Für einen dramatischen Anstieg der Leistung, mögen Sie die BulkInsert Methode in dem SQL-Namensraum untersuchen. Ich hatte „reload“ Prozesse, die von 2+ Stunden ging ein paar Dutzend Tabellen wiederherstellen bis zu 31 Sekunden, nachdem ich Bulk Import implementiert.

Das am besten erreicht, so etwas wie der bcp-Befehl werden könnte. Wenn das nicht verfügbar ist, sind die oben genannten Vorschläge über die Verwendung von Bulk Ihre beste Wette. Sie machen 500.000 Rundreisen in die Datenbank und 500.000 Einträge in die Protokolldateien zu schreiben, keinen Platz zu erwähnen, die in der Protokolldatei zugeordnet werden muss, die Tabelle und die Indizes.

Wenn Sie in einer Reihenfolge sind das Einfügen, die von Ihrem gruppierten Index unterscheiden, müssen Sie auch mit der Zeit beschäftigen erfordert die physikalischen Daten auf der Festplatte neu zu organisieren. Es gibt viele Variablen, die möglicherweise hier Ihre Abfrage langsamer ausgeführt werden machen könnte, als Sie es möchten.

~ 10.000 Transaktionen pro Sekunde ist nicht schrecklich für einzelne Einsätze von Roundtripping Code kommenden /

Bulk = bcp von einer Erlaubnis

Sie können die INSERTs Charge zu reduzieren Rundreisen SQLDataAdaptor.UpdateBatchSize = 10000 gibt 50 Umläufe

Sie haben noch 500k Einsätze obwohl ...

Artikel

MSDN

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top