Diese Frage wird aufgrund eines Vorschlags in den Kommentaren von Stack Overflow erneut gestellt. Wir entschuldigen uns für die Vervielfältigung.
Fragen
Frage 1: Wie kann ich MySQL optimieren, um die Geschwindigkeit des LOAD DATA INFILE-Aufrufs zu erhöhen, wenn die Größe der Datenbanktabelle zunimmt?
Frage 2: Würde die Verwendung eines Computerclusters zum Laden verschiedener CSV-Dateien, zur Verbesserung der Leistung oder zum Beenden der Leistung führen? (Dies ist meine Benchmarking-Aufgabe für morgen unter Verwendung der Ladedaten und Masseneinsätze.)
Tor
Wir probieren verschiedene Kombinationen von Feature-Detektoren und Clustering-Parametern für die Bildsuche aus. Daher müssen wir in der Lage sein, zeitnah große Datenbanken zu erstellen.
Maschineninfo
Die Maschine hat 256 GB RAM und es sind weitere 2 Maschinen mit der gleichen RAM-Menge verfügbar, wenn es eine Möglichkeit gibt, die Erstellungszeit durch Verteilen der Datenbank zu verbessern.
Tabellenschema
Das Tabellenschema sieht so aus
+---------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+------------------+------+-----+---------+----------------+
| match_index | int(10) unsigned | NO | PRI | NULL | |
| cluster_index | int(10) unsigned | NO | PRI | NULL | |
| id | int(11) | NO | PRI | NULL | auto_increment |
| tfidf | float | NO | | 0 | |
+---------------+------------------+------+-----+---------+----------------+
hergestellt mit
CREATE TABLE test
(
match_index INT UNSIGNED NOT NULL,
cluster_index INT UNSIGNED NOT NULL,
id INT NOT NULL AUTO_INCREMENT,
tfidf FLOAT NOT NULL DEFAULT 0,
UNIQUE KEY (id),
PRIMARY KEY(cluster_index,match_index,id)
)engine=innodb;
Bisheriges Benchmarking
Der erste Schritt bestand darin, Masseneinfügungen mit dem Laden aus einer Binärdatei in eine leere Tabelle zu vergleichen.
It took: 0:09:12.394571 to do 4,000 inserts with 5,000 rows per insert
It took: 0:03:11.368320 seconds to load 20,000,000 rows from a csv file
In Anbetracht des Leistungsunterschieds, den ich beim Laden der Daten aus einer binären CSV-Datei festgestellt habe, habe ich zuerst Binärdateien mit 100 KByte, 1 MByte, 20 MByte und 200 MByte Zeilen mit dem folgenden Aufruf geladen.
LOAD DATA INFILE '/mnt/tests/data.csv' INTO TABLE test;
Ich habe das Laden der 200M-Zeilen-Binärdatei (~ 3 GB CSV-Datei) nach 2 Stunden abgebrochen.
Also habe ich ein Skript ausgeführt, um die Tabelle zu erstellen und eine andere Anzahl von Zeilen aus einer Binärdatei einzufügen. Dann habe ich die Tabelle gelöscht (siehe Grafik unten).
Es dauerte ungefähr 7 Sekunden, um 1 Million Zeilen aus der Binärdatei einzufügen. Als Nächstes habe ich mich entschieden, das Einfügen von jeweils 1 Million Zeilen zu vergleichen, um festzustellen, ob es bei einer bestimmten Datenbankgröße zu einem Engpass kommen würde. Sobald die Datenbank ungefähr 59 Millionen Zeilen erreicht hat, ist die durchschnittliche Einfügezeit auf ungefähr 5.000 / Sekunde gesunken
Durch Festlegen der globalen key_buffer_size = 4294967296 wurden die Geschwindigkeiten für das Einfügen kleinerer Binärdateien geringfügig verbessert. Die folgende Grafik zeigt die Geschwindigkeiten für die unterschiedliche Anzahl von Zeilen
Beim Einfügen von 1M-Zeilen wurde die Leistung jedoch nicht verbessert.
Zeilen: 1.000.000 Zeit: 0: 04: 13.761428 Einfügungen / Sek .: 3.940
vs für eine leere Datenbank
Zeilen: 1.000.000 Zeit: 0: 00: 6.339295 Einfügungen / Sek .: 315.492
Aktualisieren
Führen Sie die Ladedaten in der folgenden Reihenfolge aus, anstatt nur den Befehl load data zu verwenden
SET autocommit=0;
SET foreign_key_checks=0;
SET unique_checks=0;
LOAD DATA INFILE '/mnt/imagesearch/tests/eggs.csv' INTO TABLE test_ClusterMatches;
SET foreign_key_checks=1;
SET unique_checks=1;
COMMIT;
In Bezug auf die generierte Datenbankgröße sieht dies also recht vielversprechend aus, aber die anderen Einstellungen scheinen die Leistung des Ladedaten-Infile-Aufrufs nicht zu beeinträchtigen.
Ich habe dann versucht, mehrere Dateien von verschiedenen Computern zu laden, aber der Befehl load data infile sperrt die Tabelle, da bei den anderen Computern aufgrund der Größe der Dateien eine Zeitüberschreitung auftritt
ERROR 1205 (HY000) at line 1: Lock wait timeout exceeded; try restarting transaction
Erhöhen der Anzahl der Zeilen in einer Binärdatei
rows: 10,000,000 seconds rows: 0:01:36.545094 inserts/sec: 103578.541236
rows: 20,000,000 seconds rows: 0:03:14.230782 inserts/sec: 102970.29026
rows: 30,000,000 seconds rows: 0:05:07.792266 inserts/sec: 97468.3359978
rows: 40,000,000 seconds rows: 0:06:53.465898 inserts/sec: 96743.1659866
rows: 50,000,000 seconds rows: 0:08:48.721011 inserts/sec: 94567.8324859
rows: 60,000,000 seconds rows: 0:10:32.888930 inserts/sec: 94803.3646283
Lösung: Berechnen Sie die ID außerhalb von MySQL vor, anstatt die automatische Inkrementierung zu verwenden
Tisch bauen mit
CREATE TABLE test (
match_index INT UNSIGNED NOT NULL,
cluster_index INT UNSIGNED NOT NULL,
id INT NOT NULL ,
tfidf FLOAT NOT NULL DEFAULT 0,
PRIMARY KEY(cluster_index,match_index,id)
)engine=innodb;
mit dem SQL
LOAD DATA INFILE '/mnt/tests/data.csv' INTO TABLE test FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n';"
Wenn das Skript veranlasst wird, die Indizes vorab zu berechnen, wurde anscheinend der Leistungstreffer entfernt, wenn die Datenbank größer wird.
Update 2 - mit Speichertabellen
Etwa dreimal schneller, ohne die Kosten für das Verschieben einer In-Memory-Tabelle auf eine festplattenbasierte Tabelle zu berücksichtigen.
rows: 0 seconds rows: 0:00:26.661321 inserts/sec: 375075.18851
rows: 10000000 time: 0:00:32.765095 inserts/sec: 305202.83857
rows: 20000000 time: 0:00:38.937946 inserts/sec: 256818.888187
rows: 30000000 time: 0:00:35.170084 inserts/sec: 284332.559456
rows: 40000000 time: 0:00:33.371274 inserts/sec: 299658.922222
rows: 50000000 time: 0:00:39.396904 inserts/sec: 253827.051994
rows: 60000000 time: 0:00:37.719409 inserts/sec: 265115.500617
rows: 70000000 time: 0:00:32.993904 inserts/sec: 303086.291334
rows: 80000000 time: 0:00:33.818471 inserts/sec: 295696.396209
rows: 90000000 time: 0:00:33.534934 inserts/sec: 298196.501594
Durch das Laden der Daten in eine speicherbasierte Tabelle und anschließendes Kopieren in eine festplattenbasierte Tabelle in Blöcken ergab sich ein Overhead von 10 Minuten und 59,71 Sekunden, um 107.356.741 Zeilen mit der Abfrage zu kopieren
insert into test Select * from test2;
Das Laden von 100 Millionen Zeilen dauert ungefähr 15 Minuten. Dies entspricht in etwa dem direkten Einfügen in eine festplattenbasierte Tabelle.
id
schneller sein sollte. (Obwohl ich denke, dass Sie nicht danach suchen)Antworten:
Gute Frage - gut erklärt.
Sie haben bereits eine hohe (ish) Einstellung für den Schlüsselpuffer - aber ist das genug? Ich gehe davon aus, dass dies eine 64-Bit-Installation ist (wenn nicht, müssen Sie zuerst ein Upgrade durchführen) und nicht auf MSNT ausgeführt werden. Schauen Sie sich nach einigen Tests die Ausgabe von mysqltuner.pl an.
Um den Cache optimal zu nutzen, bietet das Stapeln / Vorsortieren der Eingabedaten möglicherweise Vorteile (die neuesten Versionen des Befehls 'sort' bieten zahlreiche Funktionen zum Sortieren großer Datensätze). Auch wenn Sie die ID-Nummern außerhalb von MySQL generieren, ist dies möglicherweise effizienter.
Angenommen (erneut), Sie möchten, dass die Ausgabemenge sich wie eine einzelne Tabelle verhält, dann haben Sie nur den Vorteil, dass Sie die Arbeit des Sortierens und Generierens von IDs verteilen - für die Sie keine weiteren Datenbanken benötigen. Wenn Sie OTOH mit einem Datenbankcluster verwenden, treten Probleme mit Konflikten auf (die Sie nur als Leistungsprobleme ansehen sollten).
Wenn Sie die Daten aufteilen und die resultierenden Datasets unabhängig voneinander verarbeiten können, erhalten Sie Leistungsvorteile - dies macht jedoch nicht die Notwendigkeit zunichte, jeden Knoten zu optimieren.
Stellen Sie sicher, dass Sie mindestens 4 GB für die sort_buffer_size haben.
Darüber hinaus hängt der Leistungsbeschränkungsfaktor von der Festplatten-E / A ab. Es gibt viele Möglichkeiten, dies zu beheben - aber Sie sollten wahrscheinlich einen gespiegelten Satz gestreifter Datensätze auf SSDs in Betracht ziehen, um eine optimale Leistung zu erzielen.
quelle
load data...
schneller ist als das Einfügen. Verwenden Sie das also.Wenn Sie wirklich elegant sein möchten, können Sie ein Multithread-Programm erstellen, um eine einzelne Datei einer Sammlung von Named Pipes zuzuführen und die Insert-Instanzen zu verwalten.
Zusammenfassend gesagt, optimieren Sie MySQL nicht so sehr, als dass Sie Ihre Arbeitslast auf MySQL optimieren.
quelle
Ich erinnere mich nicht genau an die Syntacx, aber wenn es Inno-DB ist, können Sie die Fremdschlüsselprüfung deaktivieren.
Sie können den Index auch nach dem Import erstellen. Dies kann ein echter Leistungsgewinn sein.
quelle