Antworten:
https://dev.mysql.com/doc/refman/8.0/en/insert-optimization.html
Die zum Einfügen einer Zeile erforderliche Zeit wird durch die folgenden Faktoren bestimmt, wobei die Zahlen ungefähre Proportionen angeben:
- Anschließen: (3)
- Senden einer Anfrage an den Server: (2)
- Analyseabfrage: (2)
- Zeile einfügen: (1 × Zeilengröße)
- Einfügen von Indizes: (1 × Anzahl der Indizes)
- Abschluss: (1)
Daraus sollte ersichtlich sein, dass Sie durch das Senden einer großen Anweisung einen Overhead von 7 pro Einfügeanweisung sparen, was im weiteren Lesen des Textes auch besagt:
Wenn Sie mehrere Zeilen gleichzeitig vom selben Client einfügen, verwenden Sie INSERT-Anweisungen mit mehreren VALUES-Listen, um mehrere Zeilen gleichzeitig einzufügen. Dies ist erheblich schneller (in einigen Fällen um ein Vielfaches schneller) als die Verwendung separater einzeiliger INSERT-Anweisungen.
Ich weiß, dass ich diese Frage fast zweieinhalb Jahre nach ihrer Beantwortung beantworte, aber ich wollte nur einige harte Daten aus einem Projekt bereitstellen, an dem ich gerade arbeite, was zeigt, dass es VIEL ist, tatsächlich mehrere VALUE-Blöcke pro Einfügung zu erstellen schneller als sequentielle INSERT-Anweisungen eines einzelnen VALUE-Blocks.
Der Code, den ich für diesen Benchmark in C # geschrieben habe, verwendet ODBC, um Daten aus einer MSSQL-Datenquelle (~ 19.000 Zeilen, alle werden vor Beginn des Schreibens gelesen) in den Speicher einzulesen Fügen Sie die Daten aus dem Speicher über vorbereitete Anweisungen in eine Tabelle auf einem MySQL-Server ein. Es wurde so geschrieben, dass ich die Anzahl der VALUE-Blöcke pro vorbereitetem INSERT dynamisch anpassen kann (dh n Zeilen gleichzeitig einfügen, wobei ich den Wert von n vor einem Lauf anpassen kann). Außerdem habe ich den Test ausgeführt mehrmals für jedes n.
Das Ausführen einzelner VALUE-Blöcke (z. B. jeweils 1 Zeile) dauerte 5,7 bis 5,9 Sekunden. Die anderen Werte sind wie folgt:
2 Zeilen gleichzeitig: 3,5 - 3,5 Sekunden
5 Zeilen gleichzeitig: 2,2 - 2,2 Sekunden
10 Zeilen gleichzeitig: 1,7 - 1,7 Sekunden
50 Zeilen gleichzeitig: 1,17 - 1,18 Sekunden
100 Zeilen gleichzeitig: 1,1 - 1,4 Sekunden
500 Zeilen gleichzeitig: 1,1 - 1,2 Sekunden
1000 Zeilen gleichzeitig: 1,17 - 1,17 Sekunden
Ja, schon das Bündeln von 2 oder 3 Schreibvorgängen führt zu einer dramatischen Verbesserung der Geschwindigkeit (Laufzeitverkürzung um den Faktor n), bis Sie irgendwo zwischen n = 5 und n = 10 ankommen. An diesem Punkt fällt die Verbesserung deutlich ab. und irgendwo im Bereich von n = 10 bis n = 50 wird die Verbesserung vernachlässigbar.
Ich hoffe, dies hilft den Leuten bei der Entscheidung, (a) ob die Multiprepare-Idee verwendet werden soll und (b) wie viele VALUE-Blöcke pro Anweisung erstellt werden sollen (vorausgesetzt, Sie möchten mit Daten arbeiten, die groß genug sind, um die Abfrage über die maximale Abfragegröße hinauszuschieben für MySQL, von dem ich glaube, dass es an vielen Stellen standardmäßig 16 MB groß ist, möglicherweise größer oder kleiner, abhängig vom Wert von max_allowed_packet, der auf dem Server festgelegt ist.)
Ein wichtiger Faktor ist, ob Sie eine Transaktions-Engine verwenden und ob Sie eine automatische Festschreibung aktiviert haben.
Autocommit ist standardmäßig aktiviert und Sie möchten es wahrscheinlich aktiviert lassen. Daher führt jede Einfügung, die Sie ausführen, eine eigene Transaktion aus. Dies bedeutet, dass Sie, wenn Sie eine Einfügung pro Zeile ausführen, für jede Zeile eine Transaktion festschreiben.
Unter der Annahme eines einzelnen Threads bedeutet dies, dass der Server einige Daten für JEDE REIHE mit der Disc synchronisieren muss. Es muss warten, bis die Daten einen dauerhaften Speicherort erreicht haben (hoffentlich der batteriegepufferte RAM in Ihrem RAID-Controller). Dies ist von Natur aus ziemlich langsam und wird in diesen Fällen wahrscheinlich zum begrenzenden Faktor.
Ich gehe natürlich davon aus, dass Sie eine Transaktions-Engine verwenden (normalerweise innodb) UND dass Sie die Einstellungen nicht angepasst haben, um die Haltbarkeit zu verringern.
Ich gehe auch davon aus, dass Sie einen einzelnen Thread verwenden, um diese Einfügungen durchzuführen. Die Verwendung mehrerer Threads trübt die Dinge ein wenig, da einige Versionen von MySQL in innodb über ein Arbeitsgruppen-Commit verfügen. Dies bedeutet, dass mehrere Threads, die ihre eigenen Commits ausführen, einen einzigen Schreibvorgang für das Transaktionsprotokoll gemeinsam nutzen können. Dies ist gut, da weniger Synchronisierungen mit dauerhaftem Speicher erforderlich sind .
Auf der anderen Seite ist das Ergebnis, dass Sie WIRKLICH mehrzeilige Einfügungen verwenden möchten.
Es gibt eine Grenze, über die es kontraproduktiv wird, aber in den meisten Fällen sind es mindestens 10.000 Zeilen. Wenn Sie also bis zu 1.000 Zeilen stapeln, sind Sie wahrscheinlich sicher.
Wenn Sie MyISAM verwenden, gibt es eine ganze Reihe anderer Dinge, aber ich werde Sie damit nicht langweilen. Frieden.
Im Allgemeinen ist die Anzahl der Aufrufe der Datenbank umso besser (dh schneller und effizienter). Versuchen Sie daher, die Einfügungen so zu codieren, dass Datenbankzugriffe minimiert werden. Denken Sie daran, dass jeder Datenbankzugriff, sofern Sie keinen Verbindungspool verwenden, eine Verbindung herstellen, die SQL ausführen und dann die Verbindung abbrechen muss. Ziemlich viel Aufwand!
Sie möchten vielleicht :
Je nachdem , wie gut Ihre Server Waage (seine endgültig in Ordnung mit PostgreSQl
, Oracle
und MSSQL
), tat das , was oben mit mehreren Threads und mehr Verbindungen.
Im Allgemeinen sind mehrere Einfügungen aufgrund des Verbindungsaufwands langsamer. Wenn Sie mehrere Einsätze gleichzeitig ausführen, reduzieren sich die Kosten für den Overhead pro Einsatz.
Abhängig davon, welche Sprache Sie verwenden, können Sie möglicherweise einen Stapel in Ihrer Programmier- / Skriptsprache erstellen, bevor Sie zur Datenbank gehen und jede Einfügung zum Stapel hinzufügen. Dann könnten Sie einen großen Stapel mit einem Verbindungsvorgang ausführen. Hier ist ein Beispiel in Java.
MYSQL 5.5 Eine SQL-Insert-Anweisung dauerte ~ 300 bis ~ 450 ms. Die folgenden Statistiken gelten für Inline-Anweisungen für mehrere Einfügungen.
(25492 row(s) affected)
Execution Time : 00:00:03:343
Transfer Time : 00:00:00:000
Total Time : 00:00:03:343
Ich würde sagen, Inline ist der richtige Weg :)
Es ist lächerlich, wie schlecht MySQL und MariaDB optimiert sind, wenn es um Beilagen geht. Ich habe MySQL 5.7 und Mariadb 10.3 getestet, kein wirklicher Unterschied.
Ich habe dies auf einem Server mit NVME-Festplatten, 70.000 IOPS, 1,1 GB / s Seq-Durchsatz getestet, und das ist möglicher Vollduplex (Lesen und Schreiben).
Der Server ist ebenfalls ein Hochleistungsserver.
Gab es 20 GB RAM.
Die Datenbank ist vollständig leer.
Die Geschwindigkeit, die ich erhalte, betrug 5000 Einfügungen pro Sekunde, wenn mehrzeilige Einfügungen durchgeführt wurden (versucht mit 1 MB bis zu 10 MB Datenblöcken)
Nun der Hinweis:
Wenn ich einen weiteren Thread hinzufüge und in die GLEICHEN Tabellen einfüge, habe ich plötzlich 2x5000 / Sek. Noch ein Thread und ich habe insgesamt 15000 / Sek
Beachten Sie Folgendes: Wenn Sie EINEN Thread einfügen, bedeutet dies, dass Sie nacheinander auf die Festplatte schreiben können (mit Ausnahme von Indizes). Wenn Sie Threads verwenden, verschlechtern Sie tatsächlich die mögliche Leistung, da jetzt viel mehr zufällige Zugriffe erforderlich sind. Die Realitätsprüfung zeigt jedoch, dass MySQL so schlecht optimiert ist, dass Threads sehr hilfreich sind.
Die tatsächliche Leistung, die mit einem solchen Server möglich ist, beträgt wahrscheinlich Millionen pro Sekunde. Die CPU befindet sich im Leerlauf, die Festplatte im Leerlauf.
Der Grund ist ganz klar, dass Mariadb genau wie MySQL interne Verzögerungen hat.
id
, parent_id
) VALUES (1, NULL). Einer der nächsten Wertesätze ist mit dieser Zeile verknüpft. Wenn Sie in Blöcke aufgeteilt werden und dieser Satz zu einem anderen Block gelangt, wird er möglicherweise vor dem ersten verarbeitet, wodurch der gesamte Prozess fehlschlägt. Irgendeine Idee, wie man damit umgeht?
Mehrere Einsätze sind schneller, aber es sollte nicht sein. Ein weiterer Vorteil ist das Deaktivieren von Constraints-Checks, mit denen temporäre Einfügungen viel schneller ausgeführt werden. Es spielt keine Rolle, ob Ihr Tisch es hat oder nicht. Testen Sie beispielsweise das Deaktivieren von Fremdschlüsseln und genießen Sie die Geschwindigkeit:
SET FOREIGN_KEY_CHECKS=0;
Natürlich sollten Sie es nach dem Einfügen wieder einschalten durch:
SET FOREIGN_KEY_CHECKS=1;
Dies ist eine übliche Methode zum Einfügen großer Datenmengen. Die Datenintegrität kann unterbrochen werden, daher sollten Sie sich darum kümmern, bevor Sie Fremdschlüsselprüfungen deaktivieren.