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)