Ich habe mir nun den Bericht mal durchgelesen und frage mich, mit welchen Methoden auf die DB zugegriffen wird. Ich vermute mal, mit den nativen isc-Methoden.
Als App-Entwickler stehen mir diese aber nicht zur Verfügung:
a) sie sind viel zu aufwändig
b) sie sind mit aktuellen ADO-Methoden nicht verwendbar.
Somit ist man auf entsprechende Treiber angewiesen:
- ODBC
- OLEDB
- .Net
- usw.
Diese kapseln die isc-Routinen und stellen komfortable Objekte zur Verfügung (siehe PHP

) .
Aus reiner Anwendungssicht und dem zusätzlichen Aufwand der Anwendung kann ich diese Zahlen gar nicht erreichen, was sich durchaus an der CPU-Last des FB-Servers widerspiegelt.
Genau diese Woche hatte ich in einem Nicht-FB-Scenario vergleichbare Anforderungen (zu FB komme ich dann noch):
Eine AS/400 (System i, IBM i) dient als ERP-System, dessen Daten in ein BI-System geladen werden. Da die AS/400 angeblich die Daten nicht schnell genug bereitstellen konnte wurde ein SQL-Server als DWH dazwischengeschaltet. Mittels SSIS-Paketen erfolgt nun die tägliche vollständige Synchronisation.
Diese dauert nun unendlich lange, da sich keine der beiden Seiten die technischen Möglichkeiten auch nut angesehen hat.
Jeweils separat wurde ein
IBM-Spezi und ein
Microsoft-Spezi herangezogen, die nun jeder für sich dem Anderen mitteilten, dass sein System fast nichts zu tun hat. Es wurde gar nicht erst die Methoden selber untersucht.
Nun wurde ich denn gefragt, ob ich da mal was analysieren kann.
In der Folge habe ich ein kleines .Net-Programm, dass ich bei Interesse gerne zur Verfügung stelle, dass nun native beide Seiten betrachtet.
Alles mit den offiziellen .Net-Methoden:
Fall 1)
Um den Datendurchsatz zu testen habe ich rein die DataReader nur per Read() ohne die Daten selber auszulesen, abgefragt.
Der Durchsatz entsprach meinen Erwartungen: In Abhängigkeit des jeweiligen Rowvolumens konnten über das Netz zwischen 25.000 bis 125.000 Reads / Sekunde durchgeführt werden.
Der Kunde hat dies dann mal mit 5 parallelen Abfragen durchgeführt und konnte eine Übertragungsrate von 600MBit/Sekunde erreichen.
Fall 2)
Neben dem Read() habe ich dann zusätzlich eine Miniverarbeitung durchgeführt, also per GetValues() die Daten ausgelesen.
Der Durchsatz sank damit auf ca. 10.000 - 15.000 Zeilen/Sekunde.
Dies deutet also eher auf eine schwache CPU oder Hauptspeicherleistung hin oder ein Problem der .Net-Runtime.
DIes ließ sich aber durch die 5 parallelen Jobs liniar verbessern.
Dies war die Quelle.
Nun zum SQL-Server.
Fall 1)
Daten aus der Quelle komplett lesen, und anschließend per einzelnen Insert in den SQL-Server hochladen.
Durchsatz beim Lesen siehe oben Fall2, Insert ca. 1200 Zeilen Sekunde.
Fall 2)
Daten beim Lesen linear direkt in den SQL-Server schieben, wen wunderts: Lesen und Schreiben mit ca. 1200 Zeilen/Sekunde.
Nun bietet aber der SQL-Server in .Net einen sog, SqlBulCopy an. Diesem übergibt man entweder eine DataTable oder einen Reader.
Fall 1)
Laden einer DataTable per DataTable.Load(Reader), import ca. 3500 Zeilen/Sekunde. Aufruf Bulkcopy mit Datatable als Quelle: Insert ca. 23.000 - 25.000 Zeilen/Sekunde.
Fall 2)
Laden einer DataTable per DbDataAdapter mit der Fill(DataTable) Methode, Import, oh staune, 10.000 - 15.000 Zeilen/Sekunde, also Quellgeschwindigkeit. Der Insert in den SQL-Server hat sich nicht geändert.
Fall 3)
Übergabe des Readers an den BulkCopy, Lesen/Schreiben mit 10.000 - 15.000 Zeilen/Sekunde, also wieder Quellgeschwindigkeit.
Nach Analyse des SSIS-Paketes konnte ich feststellen, dass die Methode "Lesen von Quelle, Insert je Zeile" mit dem entsprechenden Durchsatz gewählt wurde.
Liest man nun die SSIS-Doku, wird native allerdings ein BulkCopy nur beim CSV-Import unterstützt. Hä?
Gut, noch ein wenig gesucht und gefunden:
SSIS unterstützt auch den Aufruf eines Scripts, also die Verwendung von .Net-Code. Mit diesem könnte man nun die Lesegeschwindigkeit zum Upload per BulkCopy verwenden. Mit parallelen Tasks, so 8-10, kommt der SQL-Server wahrscheinlich ins Schwitzen.
Nun komme ich zu inserer allseits beliebten Firebird (2.5 in Classic-Mode, also Prozess je Connection, oder 3.0 macht da kaum Unterschied).
In meinem ETL-Prozess verwende ich noch den guten 64-Bit-ODBC-Treiber:
Fall 1)
Nur Lesen, ca. 35.000 Zeilen/Sekunde.
Fall 2)
Lesen und in Liste speichern, 9500 Zeilen/Sekunde.
Fand ich in Ordnung, aber ich dachte, .Net kann es schneller.
Noch hatte ich meine modifzierte Version 6.8.0.0.
Fall 1)
Nur Lesen, ca. 12.000 Zeilen/Sekunde.
Fall 2)
Lesen und speichern, 8500 Zeilen/Sekunde.
Nun, das fand ich doch unbefriedigend, also habe ich die Version 7.10.0.1 runtergeladen (weil ich nicht überall 4.8 habe):
Fall 1)
Nur Lesen, ca. 35.000 Zeilen Sekunde.
Fall 2)
Lesen und speichern, ca. 9.500 Zeilen/Sekunde.
Da die Quellen ja nun mal vorliegen, habe ich mir mal angesehen, was da beim GetValues() so passiert, und sie da, es werden ein paar Funktionen je Spalte aufgerufen, die je Zeile abe schon mal aufgerufen waren.
Dies habe ich dann modifiziert und die Prüfungen umgangen.
Der Fall 2) lud mir die Daten nun mit 12.000 Zeilen/Sekunde, also 30% schneller.
Dies habe ich im Firebird-Tracker bereits eingestellt.
Da gibts bestimmt noch mehr Potential, ich werde berichten, denn der Insert-Teil mit dm neueren Treiber fehlt ja noch.
Aktuell schaft mein ETL mit ODBC ca. 1.500 - 2.500 Inserts/Sekunde. Das kann noch nicht das Ende sein!