Benutzer mit den meisten Antworten
Entity Framework 6 Performance Probleme bei Insert großer Datenmengen

Frage
-
Hallo Experten,
ich habe ein großes Problem. Ich muß eine alte DBF Datenbank konvertieren (MSSQL, neues Schema). Ich habe bis jetzt immer mit ca. 1000 Inserts getestet, war nicht berauschend schnell, da es aber nur einmal durchgeführt wird, nicht das Problem.
Jetzt wollte ich den kompletten Datenbestand übernehmen (insgesamt, ca. 500.000 Datensätze). Leider wird das Programm immer langsamer, sobald die Anzahl der Inserts steigen.
Tabelle 1: Klient
Tabelle 2: Buchung (FK Klient)
Tabelle 3: Beratungsinhalt (FK Buchung)
Tabelle 4: Statistik, 5 Datensätze pro Klient (FK Klient)
Ursprünglich habe ich nach jedem Klienten (8 Datensätze pro Klient) ein contxt.savechanges() durchgeführt. Nachdem das extrem langsam war, habe ich erst nach jeweils 100 Klienten (insgesamt 800 Inserts) ein savechanges durchgeführt und dann eine neue Instanz Entitities erzeugt (context = new PXEntities()). Das hat doch eine deutliche Geschwindigkeitssteigerung gebracht, jedoch sinkt die Geschwindigkeit trotzdem, je mehr Datensätze insgesamt hinzugefügt werden. Ich bin momentan bei 7000 Klienten und es ist extrem langsam (ca. 5 Sekunden für einen Satz Daten = 8 Datensätze mit Foreign Keys).
Hat jemand eine Idee, was ich hier machen kann.
Bin für jeden Tipp dankbar,
Siegfried
Antworten
-
Hallo Siegfried,
ich würde die Tabellen der Exceldatei einmal komplett per JET und OleDbConnection auslesen und dann innerhalb der Schleife auf die jeweilige DataTable zugreifen.
Der Zugriff per Excel Automatisierung ist bei diesen Datenmengen wohl der schlechteste und langsamste Weg.
Dim ConnectionString As String = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=X:\Datei.xls;Extended Properties=Excel 8.0;" Dim Connection As New OleDbConnection( ConnectionString ) Dim DataAdapter As New OleDbDataAdapter( "SELECT * FROM [Tabelle1$A:C]", Connection ) Dim DataTable As New DataTable( "Tabelle1" ) DataAdapter.Fill( DataTable ) ... DataTable.Dispose() DataAdapter.Dispose() Connection.Close() Connection.Dispose()
Gruß, Stefan
Microsoft MVP - Visual Developer ASP/ASP.NET
http://www.asp-solutions.de/ - Consulting, Development
http://www.aspnetzone.de/ - ASP.NET Zone, die ASP.NET Community- Als Antwort vorgeschlagen Thorsten DörflerEditor Dienstag, 14. April 2015 10:13
- Als Antwort markiert Aleksander Chalabashiev Montag, 27. April 2015 15:54
Alle Antworten
-
Hallo Siegfried,
Rick Strahl hat hierzu einen IMHO sehr guten Artikel geschrieben. Arbeite den mal durch und optimier deine Einstellungen bzw. die Codeangaben wie im Artikel angegeben.
So wirklich performant wird es bei Masseninserts allerdings eher nicht werden.
---
Generell ist auch wichtig, dass innerhalb der Schleife nicht massenhaft Objekte erzeugt werden, die man ggfs. gar nicht erzeugen muss und man die Erzeugung daher auch vor der Schleife durchführen kann.
Zudem sollten Objekte, die nicht mehr benötigt werden, auch entfernt werden (bspw. per .Dispose() oder ähnlichem)
Des Weiteren achte darauf, dass Du deinen Performancetest mit einer als "Release" kompilierten Anwendung durchführst, da die Debugvariante schnell auch mal um den Faktor 10 bis 50 langsamer ist als Release. (Ggfs. auch mal in die app.config bzw. web.config schauen, ob dort noch debug="true" angegeben ist)
---
Sind das eigentlich flache Daten, die Du einfügst oder hängen die Datensätze voneinander ab (bspw. Identity Werte, die nach dem INSERT ausgelesen und dann für andere Datensätze als FK genutzt werden müssen, ...)?
Gruß, Stefan
Microsoft MVP - Visual Developer ASP/ASP.NET
http://www.asp-solutions.de/ - Consulting, Development
http://www.aspnetzone.de/ - ASP.NET Zone, die ASP.NET Community
- Bearbeitet Stefan FalzModerator Mittwoch, 8. April 2015 23:33
-
Hallo Stefan,
vielen Dank für den interessanten Link. Leider behandeln die meisten Beiträge, die man im Internet zu diesem Thema finden kann, Queries oder wenn es um Inserts geht, einfache Tabellen. Ich muß aber mehrere Tabellen mit Foreign Keys übernehmen. Ich habe oben geschrieben, dass ich 100 Klienten erstelle und dann erst ein Savechanges() durchführe. Das stimmt nicht, ich muß nach jedem Klienten ein Savechanges() durchführen, da es sonst beim Einfügen der anderen Dateien mit Abhängigkeit zum Primärschlüssel Klienten eine Exception gibt.
Trotzdem habe ich mit folgendem Code, welcher jeweils nach einem Klienten ausgeführt wird, das bis jetzt beste Ergebnis erreicht:
context.SaveChanges(); context.Dispose(); context = new PXEntities(); context.Configuration.AutoDetectChangesEnabled = false;
Aber es ist nach wie vor unglaublich langsam. In 5 Stunden ca. 10.000 Klienten übernommen, das sind etwa 80.000 Datensätze. Aber die Geschwindigkeit scheint jetzt stabil zu bleiben. Wenn auch insgesamt unbefriedigend, muß ich diese Lösung verwenden, da mir der Kunde im Nacken sitzt. Vorausgesetzt, dass es sich für die restlichen Daten nicht mehr verlangsamt.
Ich erzeuge jetzt in einer Schleife nur die absolut notwendige Anzahl von Inserts.
Ja, ich verwende Release compilierte Versionen zum Testen.
Es sind keine flachen Daten, sondern für einen Klienten gibt es insgesamt 6 Datensätze mit Abhängigkeit zum Klienten und eine Kindtabelle mit einer weiteren Abhängigkeit.
Grüße
Siegfried -
Hallo Stefan,
ich habe eine interessante Entdeckung gemacht.
Das große Problem ist das auslesen der Excel Datei. Je höher die Zeile wird um so länger dauert der Zugriff. Deshalb läuft es am Anfang passabel und wird dann immer langsamer. Vielleicht lässt sich da etwas herausholen, wenn man die Range Objekte größer anlegt. Muss ich mir aber erst ansehen.
Grüße
Siegfried
-
Hi Siegfried,
ich würde das Problem zweistufig lösen. Im ersten Schritt werden alle betroffenen Tabellen mit BulkCopy in temporäre Tabellen in den SQL Server kopieren. Im zweiten Schritt werden über StroedProcedures die endgültigen Tabellen mit den Daten aus den temporären Tabellen gefüllt. Aus Erfahrung schätze ich, dass dann der gesamte Prozess in weniger als ca. 15 Minuten realisierbar sein kann.
--
Viele Grüsse
Peter Fleischer (MVP, Partner)
Meine Homepage mit Tipps und Tricks
- Bearbeitet Peter Fleischer Freitag, 10. April 2015 07:14
-
Hallo Siegfried,
ich würde die Tabellen der Exceldatei einmal komplett per JET und OleDbConnection auslesen und dann innerhalb der Schleife auf die jeweilige DataTable zugreifen.
Der Zugriff per Excel Automatisierung ist bei diesen Datenmengen wohl der schlechteste und langsamste Weg.
Dim ConnectionString As String = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=X:\Datei.xls;Extended Properties=Excel 8.0;" Dim Connection As New OleDbConnection( ConnectionString ) Dim DataAdapter As New OleDbDataAdapter( "SELECT * FROM [Tabelle1$A:C]", Connection ) Dim DataTable As New DataTable( "Tabelle1" ) DataAdapter.Fill( DataTable ) ... DataTable.Dispose() DataAdapter.Dispose() Connection.Close() Connection.Dispose()
Gruß, Stefan
Microsoft MVP - Visual Developer ASP/ASP.NET
http://www.asp-solutions.de/ - Consulting, Development
http://www.aspnetzone.de/ - ASP.NET Zone, die ASP.NET Community- Als Antwort vorgeschlagen Thorsten DörflerEditor Dienstag, 14. April 2015 10:13
- Als Antwort markiert Aleksander Chalabashiev Montag, 27. April 2015 15:54
-
Hallo Peter,
vielen Dank für Deine Anregung. Ich habe sie erst gelesen als die Konvertierung beim Kunden schon im Laufen war. Ich werde es beim nächsten mal versuchen. Ich glaube aber nicht, dass es für diesen Datenbestand anwendbar ist, weil der Datensource nicht fehlerfrei ist und ich bei der Übernahme diverse Prüfungen und Änderungen einbauen musste. Außerdem mussten die Daten umstrukturiert werden, da die neue DB ein komplett anderes Schema hat.
Viele Grüße
Siegfried
-
Hallo Stefan,
ich habe die Konvertierung am Wochenende durchgeführt. Hatte nicht mehr die Zeit und die Nerven um noch zu experimentieren. Bis auf die deutschen Sonderzeichen hat alles funktioniert. Hier werde ich wohl auf Cell Ebene eine Konvertierung einbauen müssen.
Vielen Dank für Deine Hilfe
Siegfried