DBMS gesucht



  • Hallo zusammen,

    wir haben relativ große Konfigurationsdateien (können etwa 100.000 Zeilen lang werden, wobei jede Zeile im Prinzip ein Key/Value Paar ist. Ich habe das mal klassisch mit Postgres umgesetzt, eine Master Tabelle mit Informationen für die Konfiguration (Name, Änderungsdatum, etc.) und eine Detailtabelle mit den Konfigurationseinstellungen. Das funktioniert prinzipiell, ist uns aber mit knapp 40 Sekunden für das Einfügen von 100.000 Zeilen zu langsam. Ich habe mir verschiedene NoSQL DBMS angeschaut, aber allein das Aufsetzen einer Testumgebung ist schon eine Menge Arbeit.
    Ich habe bisher keine bis wenig Erfahrungen mit NoSQL DBMS, und die Erfahrungen, die ich gestern und heute gesammelt habe lassen mir da wenig Hoffnung.

    UpscaleDB:
    Trotz einfachster Handhabung (laut Doku muss nur eine .exe ausgeführt werden) nicht zum Laufen bekommen.

    Apache Cassandra:
    Setzt Java 8 voraus (und zwar genau Java 8). Mit dem JDK 14.0.1 stürzt die Anwendung mit einer Access Violation ab. Hab noch kein Java 8 installiert um zu gucken, ob das dann funktioniert.

    Redis:
    Aktuelle Version ist 6.05, die muss man aber mit dem MSVS selbst bauen. Version 3.2 gibt als prebuilt 64bit Binaries für Windows, die habe ich zum Testen mal installiert. Dazu den "Another Redis Desktop Manager" und probehalber ein 50MB XML Dokument importiert: Dauert mehrere Minuten, völlig inakzeptabel.

    MongoDB:
    Grad installiert und versuche über MongoDB Compass ein 50MB großes JSON zu importieren. Dauert jetzt schon über 15 Minuten und es ist kein Fortschritt zu sehen.

    Hat da jemand einen Tipp für mich? Das DBMS soll das Dokument soll nach bestimmten Parametern durchsuchen können, für Windows verfügbar sein, eine C/C++ API bieten und nach Möglichkeit kostenlos sein. Wenn man das DBMS einmalig kaufen muss ist das auch ok, aber Subscription-basierte Preismodelle wie $xx Dollar pro Monat/Jahr scheiden aus.



  • @DocShoe sagte in DBMS gesucht:

    Hallo zusammen,

    wir haben relativ große Konfigurationsdateien (können etwa 100.000 Zeilen lang werden, wobei jede Zeile im Prinzip ein Key/Value Paar ist. Ich habe das mal klassisch mit Postgres umgesetzt, eine Master Tabelle mit Informationen für die Konfiguration (Name, Änderungsdatum, etc.) und eine Detailtabelle mit den Konfigurationseinstellungen. Das funktioniert prinzipiell, ist uns aber mit knapp 40 Sekunden für das Einfügen von 100.000 Zeilen zu langsam.

    Du hast ja schon vor einigen Tagen zu diesem Thema Fragen gestellt, und ehrlich gesagt waren die Performance Probleme vorhersehbar, weil Du das RDBMS vergewaltigst und nicht so nutzt wie man es tun sollte. PostgreSQL ist ein objektrelationales RDBMS, d.h. es kennt selbst Vererbung bei Objekte und man kann diese Nutzen. D.h. Du solltest nicht nach Schema-F die Daten abkippen, sondern es müssen entsprechende Tabellen angelegt werden.

    Dann gibt es Möglichkeiten das Einfügen großer Mengen an Daten zu beschleunigen. Etwa nicht nur eine ID auf einer Sequenz holen, sondern deutlich mehr. Man kann die RDBMS Parameter von PostgeSQL tunen und auf solche Szenarien optimieren. Lies Dir dazu das Handbuch durch.



  • Wenn du es richtig machst, dann kannst du in eine relationale Datenbank wie z.B. MS-SQL Server problemlos mehrere 100.000 Zeilen pro Sekunde einfügen.
    Der Knackpunt ist: du musst mehrere Zeilen auf einmal einfügen. Und es reicht nicht dass du es innerhalb einer Transaktion machst. Du musst entweder eine spezielle "batch insert" API verwenden, oder aber mehrere Zeilen mit einem einzigen SQL Statement einfügen.

    Welche Möglichkeiten zum Batch-Insert es gibt ist abhängig von der API die du verwendest um auf den Server zuzugreifen. Wenn diese keine eigene "batch insert" Funktion hat, geht es auf jeden Fall über dynamic SQL: https://docs.microsoft.com/en-us/sql/t-sql/statements/insert-transact-sql?view=sql-server-ver15#b-inserting-multiple-rows-of-data

    SQL Server ist beim Parsen von SQL relativ flott und hat keine Probleme mit grossen Statements (und mit gross meine ich > 1MB). Ich würde empfehlen mindestens tausend Zeilen pro Statement gleichzeitig einzufügen.

    ps: Wenn die Daten in einem CSV File stehen kannst du auch einfach BULK INSERT verwenden: https://docs.microsoft.com/en-us/sql/t-sql/statements/bulk-insert-transact-sql?view=sql-server-ver15

    (Mit den meisten anderen RDBMS sollte es auch gehen, aber da mir da die Erfahrung fehlt kann ich nichts konkretes dazu sagen.)


    Was deine Erfahrungen mit Redis und MongoDB angeht: das kommt mir spanisch vor, das muss alles viel schneller gehen. Wie hast du denn dort die Config importiert? Hast du vielleicht pro Key/Value Paar ein eigenes Dokument gemacht? Das würde erklären warum das alles so langsam ist. Ein Dokument mit 100k Keys sollte aber ruck-zuck angelegt sein.



  • Mit Postgres sollte die selbe "INSERT" Syntax funktionieren wie mit MS-SQL Server: https://stackoverflow.com/a/28168010/454519

    Für Import aus Files gibt es dort das "COPY" Statement: https://www.postgresql.org/docs/current/sql-copy.html

    Und falls du dabei das Problem hast dass du die "Config ID" nicht dazugebastelt bekommst: importiere das File in einen Temp-Table, und mach dann ein INSERT-SELECT mit dem die Daten aus dem Temp-Table in den Config-Table kopierst. Im SELECT Teil des INSERT-SELECT kannst du dann die "Config ID" einfach als Konstante mit "selektieren".


    Bzw. was auch immer funktionieren sollte, unabhängig davon wie die Daten vorliegen und wie der Table aussieht:

    • Speicher die Daten programmatisch in ein passend formatiertes Temp-File
    • Importiere dieses mit COPY in einen Temp Table
    • Kopiere die Daten aus dem Temp-Table mit INSERT-SELECT
    • Lösch das Temp-File und den Temp-Table

    Trotz dem hier mehrfach umgeschaufelt wird sollte das sehr sehr viel schneller sein als 100.000 mal eine Zeile einzufügen.



  • Danke für deine Antwort, hustbaer, ich habe postgres zum Testen benutzt, weil ich eine Spieldatenbank zum Rumprobieren auf meinem Rechner habe. Generell soll der db-Zugriff über ODBC gehen, damit wir vom DBMS unabhängig sind und zumindest MS-SQL Server, PostgreSQL, MySQL/MariaDB unterstützen wollen.
    Auf die Bulk-Idee bin ich mittlerweile auch gekommen, das versuche ich gerade umzusetzen. Die IDs des primary keys sind im Moment unwichtig und müssen (noch) nicht zurückgelesen werden. Dann muss ich mich zwar selbst um das SQL-Injection Problem kümmern, aber das sollte kein Problem sein.

    Zu Redis und MongoDB:
    Beide GUI tools bieten eine Import Funktion an, wie genau die funktioniert kann ich nur über Redis sagen: Nach dem Export gab es genau ein Dokument mit entsprechendem XML Inhalt. Das ist allerdings auch eine steinalte Version (3.2, aktuell ist 6.0.5, vllt hat sich da über die Versionen was getan) Bei MongoDB kann es durchaus sein, dass der Import tatsächlich mehrere Dokumente erzeugt, das konnte ich nicht prüfen, weil der Import nicht abgeschlossen wurde. Ich kann das mal mit kleinere XML Dateien probieren.



  • Update:
    Ich habe jetzt Bulk Inserts mit 2.500 Einträgen per INSERT, damit komme ich auf etwas über 3 Sekunden für 100K Zeilen, das ist schon ganz gut und damit können wir leben, denke ich.

    Redis:
    Kleine Dateien (~500K) werden sofort als ein einzelnes Dokument in den Katalog aufgenommen, bei der 50MB Datei tut sich Redis schwer. Ich kann jetzt aber nicht sagen, ob es am GUI Tool oder dem DBMS selbst liegt. Bei MongoDB sieht´s genauso aus.



  • @DocShoe sagte in DBMS gesucht:

    40 Sekunden für das Einfügen von 100.000 Zeilen zu langsam

    Kaum zu glauben.
    Wie wärs mit SQLite oder Oracle-XE?
    Für Oracle-XE gibts ODBC&Co, einige fertige OCI Wrapper sowie ein fertiges Docker-Image. Ich kann mir nicht vorstellen, dass dein 100.000er Insert mehr als 2 Sek. dauert, selbst bei dem 2-CPU-Limit.


  • Mod

    @Wutz sagte in DBMS gesucht:

    @DocShoe sagte in DBMS gesucht:

    40 Sekunden für das Einfügen von 100.000 Zeilen zu langsam

    Kaum zu glauben.
    Wie wärs mit SQLite oder Oracle-XE?

    @DocShoe sagte in DBMS gesucht:

    Ich habe jetzt Bulk Inserts mit 2.500 Einträgen per INSERT, damit komme ich auf etwas über 3 Sekunden für 100K Zeilen, das ist schon ganz gut und damit können wir leben, denke ich.

    Ja, nee. Gleich andere Systeme empfehlen, anstatt erst einmal zu gucken, ob der Fragesteller nicht vielleicht einen Anfängerfehler bei der Benutzung gemacht hat…



  • @SeppJ
    Naja, was soll man beim einfachen Insert denn groß verkehrt machen können, die Persistierung erfolgt doch sowieso erst beim Commit. Sowas muss ein DBMS einfach können und im Zweifelsfall auch schon mal glattbügeln.
    Ich habe bei Oracle noch nie ein 40 Sek. Commit gesehen, und ich mach das schon ne ganze Weile, auch schon zu Zeiten, bei denen die Manager noch nichts von BigData gefaselt haben.



  • @Wutz: Du verstehst anscheinend nicht wie Datenbanken bzgl. Commit und Rollback arbeiten. Ein Commit ist immer sehr schnell, weil die ganzen Transaktionen schon während des Einfügens (sowie Updates oder Löschen) vorgenommen werden - nur ein Rollback dauert dann länger, weil dieser dann anhand des Transaction-Logs diese wieder umgekehrt rückgängig macht (ein Rollback sollte ja nur in Ausnahmesituationen auftreten).

    s. z.B. Overview of Transaction Management (auch wenn es dort etwas anders ausgedrückt wird).



  • Lieber @Wutz, Programmierer wie du sind schuld daran dass so viele Datenbank-gestützte Systeme so langsam sind. Danke.

    Wenn du ernsthaft meinst es wäre für gute Performance nicht nötig Batch-Processing zu machen... naja, weiss nicht was man dazu noch gross sagen soll.

    ps:

    Ich kann mir nicht vorstellen, dass dein 100.000er Insert mehr als 2 Sek. dauert, selbst bei dem 2-CPU-Limit.

    Falls du es nicht überzuckert haben solltest: Er hat nicht "ein 100.000er Insert" gemacht, er hat 100.000 Insersts mit je einer Zeile gemacht.



  • @hustbaer
    Witzbold.
    Ich hab noch nie ein Bulk-Insert gebraucht, das ist auch praxisirrelevant, da hierfür gleichgelagerte Inserts vorliegen müssten, und solch stupide Aktionen sind für mich wie gesagt irrelevant, ich rede von verschiedenen Joins >20 in jedem Statement; das wird alles von Oracle glattgebügelt, und zwar ohne irgendwelche Optimizer-Hints.
    Die wesentliche Zeit geht dabei im Netzwerk verloren, wenn der Client eben nicht in der Cloud steht; noch viel mehr bei selects statt inserts.
    Und auch dabei sind 40 Sek bei nur 100.000 Statements (oder wie du es zu belieben ausdrückst: Zeilen) unglaubwürdig, wenn es nicht gerade BLOBs sind.
    Ich kümmere mich um das Wesentliche, nämlich um die (möglichst fachliche) Optimierung der Joins und nicht um profanes Tagesgeschäft eines DBMS wie das Zusammenfassen gleichartiger Statements, die auch noch direkt hintereinander erfolgen.



  • Danke an alle für die Hinweise!

    Ich glaube, ich fasse noch mal zusammen 😉
    Ich schrub oben, dass jeder Eintrag prinzipiell ein Key/Value Paar ist. Aber auch nur prinzipiell, im Grunde soll sich der Parameter selbst beschreiben und bekommt noch ein paar Zusatzinformationen mit (Anzeigename, Kanonischer Name, Beschreibung, Minimum, Maximum, Defaultwert, Datentyp und Formatanweisungen), das sind insgesamt 13 Felder. Das ist wahrscheinlich auch der Grund, warum einige Leute hier meinen, das sei mit max. 2 Sekunden abgefrühstückt, weil sie halt nur von Key/Value ausgehen konnten. 100K Zeilen sind jetzt auch der worst case, in der Regel werden es sehr viel weniger sein. Ich bin deshalb von 100K Zeilen ausgegangen, weil´s dann nicht mehr "schlimmer" werden kann.

    Ich habe anfangs jeden Datensatz einzeln in die DB geschrieben, weil ich den INSERT so formuliert habe, dass er mir den vom DBMS zugeteilten Primary Key dem Objekt zugeordnet hat, sowas in der Art wie:

    void SomeDBInterface::insert_parameter( Parameter& param )
    {
       auto stmt = somedbmsobj.prepare( ... );
       stmt.execue();
       param.ID = stmt.field_value<unsigned int>( 0 );
    }
    

    Mit Bulk Insertions geht das jetzt nicht mehr, dazu muss ich erst alle Parameter zurücklesen, aber das macht erst mal nichts.

    SQLite scheidet aus, weil es eine zentrale DB werden soll, auf die von verschiedenen Rechnern aus zugegriffen wird. SQLIte hat kein dedizierten Server, sondern steuert Transaktionen und Concurrency über Mutexes, das geht, soweit ich weiß, nicht, wenn verschiedene Rechner auf das gleiche DB File zugreifen.

    Mit Oracle unter Windows habe ich keine guten Erfahrungen gemacht, das war viel Frickelei, musste mich aber auch nur oberflächlich damit beschäftigen. Und ich weiß auch nicht, ob die XE Variante nicht irgendwelche Einschränkungen hat, die uns hinter Probleme bereiten.



  • @Wutz sagte in DBMS gesucht:

    Ich hab noch nie ein Bulk-Insert gebraucht, das ist auch praxisirrelevant, da hierfür gleichgelagerte Inserts vorliegen müssten, und solch stupide Aktionen sind für mich wie gesagt irrelevant,

    Na schön dass du anscheinend gerne auf Fragen über Dinge antwortest die für dich irrelevant sind, und dabei dann völlig ignorierst dass deine Antwort dadurch irrelevant wird.

    Ich kümmere mich um das Wesentliche, nämlich um die (möglichst fachliche) Optimierung der Joins und nicht um profanes Tagesgeschäft eines DBMS wie das Zusammenfassen gleichartiger Statements, die auch noch direkt hintereinander erfolgen.

    Du verwendest JOINs statt 1+N SELECTs, verstehst aber nicht dass es wichtig ist mehrere INSERTs zusammenzufassen. Interessant.



  • @DocShoe sagte in DBMS gesucht:

    Ich schrub oben, dass jeder Eintrag prinzipiell ein Key/Value Paar ist. Aber auch nur prinzipiell, im Grunde soll sich der Parameter selbst beschreiben und bekommt noch ein paar Zusatzinformationen mit (Anzeigename, Kanonischer Name, Beschreibung, Minimum, Maximum, Defaultwert, Datentyp und Formatanweisungen), das sind insgesamt 13 Felder.

    Das ist Objektserialisierung nach Schema-F, und Dein Grund für die Probleme. Keys brauchst Du bei korrekter Nutzung des RDBMS gar nicht, weil die Keys die Struktur in der DB ist. Die Prüfung der Werte übernimmt ebenfalls das RDBMS. Es macht Arbeit die DB Struktur zu pflegen, aber dafür gewinnt man sehr viel mehr Performance.

    Ich habe anfangs jeden Datensatz einzeln in die DB geschrieben, weil ich den INSERT so formuliert habe, dass er mir den vom DBMS zugeteilten Primary Key dem Objekt zugeordnet hat, sowas in der Art wie:

    void SomeDBInterface::insert_parameter( Parameter& param )
    {
       auto stmt = somedbmsobj.prepare( ... );
       stmt.execue();
       param.ID = stmt.field_value<unsigned int>( 0 );
    }
    

    Autsch, mit MySQL gelernt? Richtige RDBMs haben Sequenzen, daraus holt man sich die IDs für die Primary Keys vor dem INSERT und führt dann den INSERT mit der geholten ID durch.



  • @john-0
    Irgendwie erinnern mich deine Antworten immer an MS-Doku. Technisch korrekt, aber irgendwie nicht zu gebrauchen.



  • @DocShoe sagte in DBMS gesucht:

    @john-0
    Irgendwie erinnern mich deine Antworten immer an MS-Doku. Technisch korrekt, aber irgendwie nicht zu gebrauchen.

    Du bist auf einem Tripp der die Dich in die komplett falsche Ecke führt, sehen willst Du das aber nicht. Weshalb nutzt Du überhaupt ein RDBMS, wenn es eine simple Textdatei auf einem Fileserver auch täte bzw. ein Blob im RDBMs? Die Vorteile eines RDBMS nutzt Du durch Deiner Art und Weise der Nutzung des selben definitiv nicht. Key-Value-Paare legt man in keinem RDBMS ab, denn die „Keys“ gehören in die Tables d.h. der Struktur der Datenbank.

    Anstatt nun die Bohrhammer dafür zu nutzen ein Loch zu bohren, dieses anschließend aus zu saugen, dann einen Dübel rein zu stecken und anschließend die Schraube einzuschrauben, hast Du Dich dazu entschlossen mit dem Hammer die Schraube in die Wand zu schlagen. Ja, das geht, aber sinnvoll ist es nicht.


Log in to reply