Ich muss mit dem BULK INSERT
Befehl von SQL Server einen ziemlich alten Code neu schreiben, da sich das Schema geändert hat, und mir ist der Gedanke gekommen, dass ich vielleicht stattdessen über einen Wechsel zu einer gespeicherten Prozedur mit einem TVP nachdenken sollte, aber ich frage mich, welchen Effekt dies hat es könnte auf Leistung haben.
Einige Hintergrundinformationen, die erklären könnten, warum ich diese Frage stelle:
Die Daten kommen tatsächlich über einen Webdienst ein. Der Webdienst schreibt eine Textdatei in einen freigegebenen Ordner auf dem Datenbankserver, der wiederum eine ausführt
BULK INSERT
. Dieser Prozess wurde ursprünglich auf SQL Server 2000 implementiert, und zu der Zeit gab es wirklich keine andere Alternative, als ein paar hundertINSERT
Anweisungen auf dem Server abzulegen, was eigentlich der ursprüngliche Prozess war und eine Leistungskatastrophe darstellte.Die Daten werden in großen Mengen in eine permanente Staging-Tabelle eingefügt und dann in eine viel größere Tabelle zusammengeführt (wonach sie aus der Staging-Tabelle gelöscht werden).
Die einzufügende Datenmenge ist "groß", aber nicht "riesig" - normalerweise einige hundert Zeilen, in seltenen Fällen vielleicht 5-10.000 Zeilen oben. Daher mein Bauchgefühl ist , dass
BULK INSERT
eine nicht protokollierte Operation ist nicht machen , dass die großen Unterschied (aber natürlich bin ich nicht sicher, daher die Frage).Das Einfügen ist tatsächlich Teil eines viel größeren Pipeline-Batch-Prozesses und muss viele Male hintereinander erfolgen. daher Leistung ist entscheidend.
Die Gründe, warum ich das BULK INSERT
durch ein TVP ersetzen möchte, sind:
Das Schreiben der Textdatei über NetBIOS kostet wahrscheinlich bereits einige Zeit und ist aus architektonischer Sicht ziemlich grausam.
Ich glaube, dass die Staging-Tabelle beseitigt werden kann (und sollte). Der Hauptgrund dafür ist, dass die eingefügten Daten gleichzeitig mit dem Einfügen für einige andere Aktualisierungen verwendet werden müssen und es weitaus teurer ist, die Aktualisierung aus der massiven Produktionstabelle zu versuchen, als eine fast leere Bereitstellung Tabelle. Mit einem TVP, der Parameter im Grunde ist die Zwischenspeichertabelle, ich alles tun , kann ich damit vor wollen / nach dem Haupteinsatz.
Ich könnte die Dupe-Überprüfung, den Bereinigungscode und den gesamten mit Masseneinfügungen verbundenen Overhead so gut wie beseitigen.
Sie müssen sich keine Gedanken über Sperrenkonflikte in der Staging-Tabelle oder in der Tempdb machen, wenn der Server einige dieser Transaktionen gleichzeitig erhält (wir versuchen, dies zu vermeiden, aber es passiert).
Ich werde dies natürlich profilieren, bevor ich etwas in Produktion bringe, aber ich dachte, es wäre eine gute Idee, zuerst nachzufragen, bevor ich die ganze Zeit damit verbringe, zu prüfen, ob jemand strenge Warnungen bezüglich der Verwendung von TVPs für diesen Zweck hat.
Also - für alle, die mit SQL Server 2008 vertraut genug sind, um dies zu versuchen oder zumindest zu untersuchen, wie lautet das Urteil? Schneiden Beilagen für Einsätze von beispielsweise einigen hundert bis einigen tausend Zeilen, die ziemlich häufig vorkommen, den Senf? Gibt es einen signifikanten Leistungsunterschied zu Bulk-Einsätzen?
Update: Jetzt mit 92% weniger Fragezeichen!
(AKA: Testergebnisse)
Das Endergebnis ist jetzt nach einem 36-stufigen Bereitstellungsprozess in der Produktion. Beide Lösungen wurden ausgiebig getestet:
- Herausreißen des Codes für freigegebene Ordner und
SqlBulkCopy
direkte Verwendung der Klasse; - Wechseln zu einer gespeicherten Prozedur mit TVPs.
Damit die Leser eine Vorstellung davon bekommen, was genau getestet wurde, um Zweifel an der Zuverlässigkeit dieser Daten auszuräumen, finden Sie hier eine detailliertere Erläuterung der tatsächlichen Funktionsweise dieses Importvorgangs :
Beginnen Sie mit einer zeitlichen Datensequenz, die normalerweise etwa 20-50 Datenpunkte umfasst (obwohl sie manchmal einige hundert betragen kann).
Führen Sie eine ganze Reihe verrückter Verarbeitungen durch, die größtenteils unabhängig von der Datenbank sind. Dieser Prozess ist parallelisiert, so dass ungefähr 8-10 der Sequenzen in (1) gleichzeitig verarbeitet werden. Jeder parallele Prozess erzeugt 3 zusätzliche Sequenzen.
Nehmen Sie alle 3 Sequenzen und die Originalsequenz und kombinieren Sie sie zu einem Stapel.
Kombinieren Sie die Stapel aus allen 8-10 jetzt abgeschlossenen Verarbeitungsaufgaben zu einem großen Super-Stapel.
Importieren Sie es entweder mit der
BULK INSERT
Strategie (siehe nächster Schritt) oder mit der TVP-Strategie (fahren Sie mit Schritt 8 fort).Verwenden Sie die
SqlBulkCopy
Klasse, um den gesamten Super-Batch in 4 permanente Staging-Tabellen zu speichern.Führen Sie eine gespeicherte Prozedur aus, die (a) eine Reihe von Aggregationsschritten für 2 der Tabellen einschließlich mehrerer
JOIN
Bedingungen ausführt und (b) eineMERGE
für 6 Produktionstabellen unter Verwendung der aggregierten und nicht aggregierten Daten ausführt . (Fertig)ODER
Generieren Sie 4
DataTable
Objekte mit den zusammenzuführenden Daten. 3 von ihnen enthalten CLR-Typen, die von ADO.NET-TVPs leider nicht ordnungsgemäß unterstützt werden. Daher müssen sie als Zeichenfolgendarstellungen eingefügt werden, was die Leistung etwas beeinträchtigt.Führen Sie die TVPs einer gespeicherten Prozedur zu, die im Wesentlichen dieselbe Verarbeitung wie (7) ausführt, jedoch direkt mit den empfangenen Tabellen. (Fertig)
Die Ergebnisse waren ziemlich nahe beieinander, aber der TVP-Ansatz schnitt letztendlich im Durchschnitt besser ab, selbst wenn die Daten 1000 Zeilen um einen kleinen Betrag überstiegen.
Beachten Sie, dass dieser Importvorgang viele tausend Mal hintereinander ausgeführt wird. Daher war es sehr einfach, eine durchschnittliche Zeit zu ermitteln, indem einfach gezählt wurde, wie viele Stunden (ja, Stunden) erforderlich waren, um alle Zusammenführungen abzuschließen.
Ursprünglich dauerte eine durchschnittliche Zusammenführung fast genau 8 Sekunden (unter normaler Last). Durch Entfernen des NetBIOS-Kludges und Umschalten SqlBulkCopy
auf wurde die Zeit auf fast genau 7 Sekunden reduziert. Durch die Umstellung auf TVPs wurde die Zeit auf 5,2 Sekunden pro Charge weiter reduziert . Das ist eine 35% ige Verbesserung des Durchsatzes für einen Prozess, dessen Laufzeit in Stunden gemessen wird - also überhaupt nicht schlecht. Es ist auch eine Verbesserung von ~ 25% gegenüber SqlBulkCopy
.
Ich bin eigentlich ziemlich zuversichtlich, dass die wahre Verbesserung deutlich mehr war. Während des Tests stellte sich heraus, dass die endgültige Zusammenführung nicht mehr der kritische Pfad war. Stattdessen begann der Webdienst, der die gesamte Datenverarbeitung durchführte, unter der Anzahl der eingehenden Anforderungen zu knicken. Weder die CPU noch die Datenbank-E / A waren wirklich voll und es gab keine signifikante Sperraktivität. In einigen Fällen wurde zwischen aufeinanderfolgenden Zusammenführungen eine Lücke von einigen Sekunden im Leerlauf angezeigt. Es gab eine leichte Lücke, die jedoch bei der Verwendung viel kleiner war (etwa eine halbe Sekunde) SqlBulkCopy
. Aber ich nehme an, das wird eine Geschichte für einen anderen Tag.
Schlussfolgerung: Tabellenwertige Parameter sind BULK INSERT
bei komplexen Import- und Transformationsprozessen, die mit mittelgroßen Datensätzen ausgeführt werden, tatsächlich besser als Operationen.
Ich möchte noch einen weiteren Punkt hinzufügen, um die Besorgnis einiger Leute zu lindern, die Pro-Staging-Tische sind. In gewisser Weise ist dieser gesamte Service ein riesiger Staging-Prozess. Jeder Schritt des Prozesses wird stark geprüft, sodass wir keine Staging-Tabelle benötigen, um festzustellen, warum eine bestimmte Zusammenführung fehlgeschlagen ist (obwohl dies in der Praxis fast nie der Fall ist). Alles was wir tun müssen, ist ein Debug-Flag im Service zu setzen und es wird zum Debugger brechen oder seine Daten in eine Datei anstelle der Datenbank sichern.
Mit anderen Worten, wir haben bereits mehr als genug Einblick in den Prozess und benötigen nicht die Sicherheit eines Staging-Tisches. der einzige Grund , warum ich war die Staging - Tabelle in erster Linie hatte Dreschen auf alle die zu vermeiden INSERT
und UPDATE
Aussagen , die wir sonst verwenden gehabt hätten. Im ursprünglichen Prozess lebten die Staging-Daten ohnehin nur für Bruchteile einer Sekunde in der Staging-Tabelle, sodass sie in Bezug auf Wartung / Wartbarkeit keinen Mehrwert erbrachten.
Beachten Sie auch, dass wir nicht jede einzelne BULK INSERT
Operation durch TVPs ersetzt haben. Einige Vorgänge, die sich mit größeren Datenmengen befassen und / oder mit den Daten nichts Besonderes tun müssen, als sie in die Datenbank zu werfen, werden weiterhin verwendet SqlBulkCopy
. Ich behaupte nicht, dass TVPs ein Allheilmittel für die Leistung sind, sondern nur, dass sie SqlBulkCopy
in diesem speziellen Fall erfolgreich waren und mehrere Transformationen zwischen der anfänglichen Inszenierung und der endgültigen Zusammenführung beinhalteten.
Da haben Sie es also. Punkt geht an TToni, um den relevantesten Link zu finden, aber ich schätze auch die anderen Antworten. Danke noch einmal!
Antworten:
Ich habe nicht wirklich Erfahrung mit TVP noch, aber es ist ein schöner Leistungsvergleich INSERT Diagramm gegen MASSE in MSDN hier .
Sie sagen, dass BULK INSERT höhere Startkosten hat, danach aber schneller ist. In einem Remote-Client-Szenario ziehen sie die Linie in etwa 1000 Zeilen (für "einfache" Serverlogik). Nach ihrer Beschreibung zu urteilen, würde ich sagen, dass Sie mit der Verwendung von TVPs in Ordnung sein sollten. Der Leistungseinbruch - falls vorhanden - ist wahrscheinlich vernachlässigbar und die architektonischen Vorteile scheinen sehr gut zu sein.
Bearbeiten: Nebenbei bemerkt, Sie können die serverlokale Datei vermeiden und trotzdem eine Massenkopie verwenden, indem Sie das SqlBulkCopy-Objekt verwenden. Füllen Sie einfach eine DataTable aus und geben Sie sie in die "WriteToServer" -Methode einer SqlBulkCopy-Instanz ein. Einfach zu bedienen und sehr schnell.
quelle
Die Tabelle, die in Bezug auf den in der Antwort von @ TToni angegebenen Link erwähnt wird, muss im Kontext betrachtet werden. Ich bin nicht sicher, wie viel tatsächliche Forschung in diese Empfehlungen geflossen ist (beachten Sie auch, dass die Tabelle nur in den
2008
und2008 R2
Versionen dieser Dokumentation verfügbar zu sein scheint ).Auf der anderen Seite gibt es dieses Whitepaper des SQL Server-Kundenberatungsteams: Maximieren des Durchsatzes mit TVP
Ich verwende TVPs seit 2009 und habe zumindest meiner Erfahrung nach festgestellt, dass TVPs für etwas anderes als das einfache Einfügen in eine Zieltabelle ohne zusätzliche logische Anforderungen (was selten der Fall ist) in der Regel die bessere Option sind.
Ich vermeide Staging-Tabellen, da die Datenüberprüfung auf App-Ebene erfolgen sollte. Durch die Verwendung von TVPs ist dies leicht zu berücksichtigen, und die TVP-Tabellenvariable in der gespeicherten Prozedur ist naturgemäß eine lokalisierte Staging-Tabelle (daher kein Konflikt mit anderen Prozessen, die zur gleichen Zeit ausgeführt werden, wie Sie sie erhalten, wenn Sie eine echte Tabelle für das Staging verwenden ).
In Bezug auf die in der Frage durchgeführten Tests könnte gezeigt werden, dass sie noch schneller sind als ursprünglich festgestellt:
IEnumerable<SqlDataRecord>
Schnittstelle ist schneller und benötigt weniger Speicher, da Sie die Sammlung nicht nur zum Senden an die Datenbank duplizieren. Ich habe dies an folgenden Stellen dokumentiert:OPTION (RECOMPILE)
#
) und kopieren Sie den Inhalt des TVP in die temporäre Tabellequelle
Ich denke, ich würde mich immer noch an einen Bulk-Insert-Ansatz halten. Möglicherweise wird Tempdb immer noch von einem TVP mit einer angemessenen Anzahl von Zeilen getroffen. Dies ist mein Bauchgefühl. Ich kann nicht sagen, dass ich die Leistung der Verwendung von TVP getestet habe (ich bin jedoch daran interessiert, auch andere Eingaben zu hören).
Sie erwähnen nicht, ob Sie .NET verwenden, aber der Ansatz, den ich zur Optimierung früherer Lösungen gewählt habe, bestand darin, eine große Datenmenge mit der SqlBulkCopy- Klasse zu laden. Sie müssen die Daten vorher nicht in eine Datei schreiben Laden Sie der SqlBulkCopy- Klasse (z. B.) einfach eine DataTable - das ist der schnellste Weg, um Daten in die Datenbank einzufügen. 5-10K Zeilen sind nicht viel, ich habe dies für bis zu 750K Zeilen verwendet. Ich vermute, dass es mit ein paar hundert Zeilen im Allgemeinen keinen großen Unterschied machen würde, einen TVP zu verwenden. Aber eine Vergrößerung wäre meiner Meinung nach begrenzt.
Vielleicht würde Ihnen die neue MERGE- Funktionalität in SQL 2008 zugute kommen?
Wenn Ihre vorhandene Staging-Tabelle eine einzelne Tabelle ist, die für jede Instanz dieses Prozesses verwendet wird, und Sie sich Sorgen über Konflikte usw. machen, haben Sie darüber nachgedacht, jedes Mal eine neue "temporäre", aber physische Staging-Tabelle zu erstellen und diese dann zu löschen, wenn dies der Fall ist fertig mit?
Beachten Sie, dass Sie das Laden in diese Staging-Tabelle optimieren können, indem Sie sie ohne Indizes füllen. Fügen Sie dann nach dem Auffüllen alle erforderlichen Indizes zu diesem Zeitpunkt hinzu (FILLFACTOR = 100 für eine optimale Leseleistung, da es zu diesem Zeitpunkt nicht aktualisiert wird).
quelle
SqlBulkCopy
und wurde einfach nie geändert. Vielen Dank, dass Sie mich daran erinnert haben. Es könnte sich lohnen, es noch einmal zu besuchen.MERGE
wird auch bereits ausgiebig verwendet, und temporäre Tabellen wurden bereits einmal ausprobiert, erwiesen sich jedoch als langsamer und schwieriger zu verwalten. Danke für die Eingabe!Inszenierungstische sind gut! Wirklich, ich würde es nicht anders machen wollen. Warum? Weil sich Datenimporte unerwartet ändern können (und oft auf eine Weise, die Sie nicht vorhersehen können, wie die Zeit, als die Spalten noch Vor- und Nachname genannt wurden, aber die Vorname-Daten in der Nachname-Spalte hatten, um beispielsweise ein Beispiel nicht auszuwählen nach dem Zufallsprinzip.) Einfache Untersuchung des Problems mit einer Staging-Tabelle, sodass Sie genau sehen können, welche Daten in den vom Import behandelten Spalten enthalten waren. Ich denke, es ist schwieriger zu finden, wenn Sie eine In-Memory-Tabelle verwenden. Ich kenne viele Leute, die wie ich ihren Lebensunterhalt mit Importen verdienen, und alle empfehlen die Verwendung von Staging-Tabellen. Ich vermute, dass es dafür einen Grund gibt.
Das weitere Korrigieren einer kleinen Schemaänderung an einem Arbeitsprozess ist einfacher und weniger zeitaufwendig als das Neugestalten des Prozesses. Wenn es funktioniert und niemand bereit ist, Stunden für die Änderung zu zahlen, beheben Sie nur das, was aufgrund der Schemaänderung behoben werden muss. Indem Sie den gesamten Prozess ändern, führen Sie weitaus mehr potenzielle neue Fehler ein, als wenn Sie eine kleine Änderung an einem vorhandenen, getesteten Arbeitsprozess vornehmen.
Und wie werden Sie alle Datenbereinigungsaufgaben abschaffen? Sie können sie anders machen, aber sie müssen noch gemacht werden. Auch hier ist es sehr riskant, den von Ihnen beschriebenen Prozess zu ändern.
Persönlich klingt es für mich so, als wären Sie nur beleidigt, wenn Sie ältere Techniken anwenden, anstatt die Chance zu bekommen, mit neuen Spielsachen zu spielen. Sie scheinen keine wirkliche Grundlage zu haben, um etwas anderes ändern zu wollen, als Bulk Insert ist also 2000.
quelle