Verwenden von MySQL 5.6 mit der InnoDB-Speicher-Engine für die meisten Tabellen. Die Größe des InnoDB-Pufferpools beträgt 15 GB, und die Innodb DB + -Indizes liegen bei etwa 10 GB. Der Server verfügt über 32 GB RAM und führt Cent OS 7 x64 aus.
Ich habe einen großen Tisch, der über 10 Millionen Datensätze enthält.
Ich erhalte alle 24 Stunden eine aktualisierte Dump-Datei von einem Remote-Server. Die Datei ist im CSV-Format. Ich habe keine Kontrolle über dieses Format. Die Datei ist ~ 750 MB groß. Ich habe versucht, Daten zeilenweise in eine MyISAM-Tabelle einzufügen, und es dauerte 35 Minuten.
Ich muss nur 3 Werte pro Zeile von 10-12 aus der Datei entnehmen und in der Datenbank aktualisieren.
Was ist der beste Weg, um so etwas zu erreichen?
Ich muss das täglich tun.
Momentan sieht Flow so aus:
- mysqli_begin_transaction
- Dump-Datei Zeile für Zeile lesen
- Aktualisieren Sie jeden Datensatz Zeile für Zeile.
- mysqli_commit
Die oben genannten Vorgänge dauern ca. 30-40 Minuten. Dabei werden noch weitere Aktualisierungen durchgeführt, die ich erhalten kann
Wartezeitüberschreitung der Sperre überschritten; Starten Sie die Transaktion erneut
Update 1
Daten laden in neue Tabelle mit LOAD DATA LOCAL INFILE
. In MyISAM hat es gedauert, 38.93 sec
während es in InnoDB 7 Minuten 5,21 Sekunden gedauert hat. Dann habe ich gemacht:
UPDATE table1 t1, table2 t2
SET
t1.field1 = t2.field1,
t1.field2 = t2.field2,
t1.field3 = t2.field3
WHERE t1.field10 = t2.field10
Query OK, 434914 rows affected (22 hours 14 min 47.55 sec)
Update 2
Gleiches Update mit Join-Abfrage
UPDATE table1 a JOIN table2 b
ON a.field1 = b.field1
SET
a.field2 = b.field2,
a.field3 = b.field3,
a.field4 = b.field4
(14 hours 56 min 46.85 sec)
Erläuterungen zu Fragen in Kommentaren:
- Ungefähr 6% der Zeilen in der Tabelle werden von der Datei aktualisiert, aber manchmal können es bis zu 25% sein.
- Es gibt Indizes für die Felder, die aktualisiert werden. Die Tabelle enthält 12 Indizes, und 8 Indizes enthalten die Aktualisierungsfelder.
- Es ist nicht erforderlich , das Update in einer Transaktion durchzuführen. Es kann einige Zeit dauern, aber nicht länger als 24 Stunden. Ich versuche, es in 1 Stunde zu erledigen, ohne die gesamte Tabelle zu sperren, da ich später den von dieser Tabelle abhängigen Sphinx-Index aktualisieren muss. Es spielt keine Rolle, ob die Schritte länger dauern, solange die Datenbank für andere Aufgaben verfügbar ist.
- Ich könnte das CSV-Format in einem Vorverarbeitungsschritt ändern. Das einzige, was zählt, ist ein schnelles Update und ohne Sperren.
- Tabelle 2 ist MyISAM. Dies ist die neu erstellte Tabelle aus der CSV-Datei, die die Ladedaten-Datei verwendet. MYI Dateigröße beträgt 452 MB. Tabelle 2 ist in der Spalte field1 indiziert.
- MYD der MyISAM-Tabelle ist 663 MB.
Update 3:
Hier finden Sie weitere Details zu beiden Tabellen.
CREATE TABLE `content` (
`hash` char(40) CHARACTER SET ascii NOT NULL DEFAULT '',
`title` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
`og_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
`keywords` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '',
`files_count` smallint(5) unsigned NOT NULL DEFAULT '0',
`more_files` smallint(5) unsigned NOT NULL DEFAULT '0',
`files` varchar(255) COLLATE utf8_unicode_ci NOT NULL DEFAULT '0',
`category` smallint(3) unsigned NOT NULL DEFAULT '600',
`size` bigint(19) unsigned NOT NULL DEFAULT '0',
`downloaders` int(11) NOT NULL DEFAULT '0',
`completed` int(11) NOT NULL DEFAULT '0',
`uploaders` int(11) NOT NULL DEFAULT '0',
`creation_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`upload_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`last_updated` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`vote_up` int(11) unsigned NOT NULL DEFAULT '0',
`vote_down` int(11) unsigned NOT NULL DEFAULT '0',
`comments_count` int(11) NOT NULL DEFAULT '0',
`imdb` int(8) unsigned NOT NULL DEFAULT '0',
`video_sample` tinyint(1) NOT NULL DEFAULT '0',
`video_quality` tinyint(2) NOT NULL DEFAULT '0',
`audio_lang` varchar(127) CHARACTER SET ascii NOT NULL DEFAULT '',
`subtitle_lang` varchar(127) CHARACTER SET ascii NOT NULL DEFAULT '',
`verified` tinyint(1) unsigned NOT NULL DEFAULT '0',
`uploader` int(11) unsigned NOT NULL DEFAULT '0',
`anonymous` tinyint(1) NOT NULL DEFAULT '0',
`enabled` tinyint(1) unsigned NOT NULL DEFAULT '0',
`tfile_size` int(11) unsigned NOT NULL DEFAULT '0',
`scrape_source` tinyint(1) unsigned NOT NULL DEFAULT '0',
`record_num` int(11) unsigned NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`record_num`),
UNIQUE KEY `hash` (`hash`),
KEY `uploaders` (`uploaders`),
KEY `tfile_size` (`tfile_size`),
KEY `enabled_category_upload_date_verified_` (`enabled`,`category`,`upload_date`,`verified`),
KEY `enabled_upload_date_verified_` (`enabled`,`upload_date`,`verified`),
KEY `enabled_category_verified_` (`enabled`,`category`,`verified`),
KEY `enabled_verified_` (`enabled`,`verified`),
KEY `enabled_uploader_` (`enabled`,`uploader`),
KEY `anonymous_uploader_` (`anonymous`,`uploader`),
KEY `enabled_uploaders_upload_date_` (`enabled`,`uploaders`,`upload_date`),
KEY `enabled_verified_category` (`enabled`,`verified`,`category`),
KEY `verified_enabled_category` (`verified`,`enabled`,`category`)
) ENGINE=InnoDB AUTO_INCREMENT=7551163 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ROW_FORMAT=FIXED
CREATE TABLE `content_csv_dump_temp` (
`hash` char(40) CHARACTER SET ascii NOT NULL DEFAULT '',
`title` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`category_id` int(11) unsigned NOT NULL DEFAULT '0',
`uploaders` int(11) unsigned NOT NULL DEFAULT '0',
`downloaders` int(11) unsigned NOT NULL DEFAULT '0',
`verified` tinyint(1) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`hash`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
und hier ist die Aktualisierungsabfrage, die die content
Tabelle unter Verwendung von Daten aus aktualisiertcontent_csv_dump_temp
UPDATE content a JOIN content_csv_dump_temp b
ON a.hash = b.hash
SET
a.uploaders = b.uploaders,
a.downloaders = b.downloaders,
a.verified = b.verified
Update 4:
Alle oben genannten Tests wurden auf einer Testmaschine durchgeführt, aber jetzt habe ich dieselben Tests auf der Produktionsmaschine durchgeführt, und die Abfragen sind sehr schnell.
mysql> UPDATE content_test a JOIN content_csv_dump_temp b
-> ON a.hash = b.hash
-> SET
-> a.uploaders = b.uploaders,
-> a.downloaders = b.downloaders,
-> a.verified = b.verified;
Query OK, 2673528 rows affected (7 min 50.42 sec)
Rows matched: 7044818 Changed: 2673528 Warnings: 0
Ich entschuldige mich für meinen Fehler. Es ist besser, Join anstelle jedes Datensatzupdates zu verwenden. Jetzt versuche ich, mpre mit dem von rick_james vorgeschlagenen Index zu verbessern. Wird aktualisiert, sobald das Benchmarking abgeschlossen ist.
INDEX(field2, field3, field4)
(in beliebiger Reihenfolge)? Bitte zeigen Sie unsSHOW CREATE TABLE
.UPDATEs
. Bitte teilen Sie uns genau mit, wie die einfache Anweisung zum Aktualisieren der Tabelle aus den CSV-Daten aussieht. Dann können wir Ihnen möglicherweise dabei helfen, eine Technik zu entwickeln, die Ihren Anforderungen entspricht.update
, und überprüfen Sie bitte aktualisierte Frage., DankeAntworten:
Nach meiner Erfahrung würde ich LOAD DATA INFILE verwenden , um Ihre CSV-Datei zu importieren.
Beispiel Ich habe im Internet Load Data Beispiel gefunden . Ich habe dieses Beispiel auf meiner Box getestet und es hat gut funktioniert
Beispieltabelle
Beispiel CSV-Datei
Import-Anweisung, die von der MySQL-Konsole ausgeführt werden soll
Ergebnis
IGNORE ignoriert einfach die erste Zeile, die Spaltenüberschriften sind.
Nach IGNORE geben wir die zu importierenden Spalten an (überspringen Spalte 2), die einem der Kriterien in Ihrer Frage entsprechen.
Hier ist ein weiteres Beispiel direkt von Oracle: LOAD DATA INFILE-Beispiel
Dies sollte ausreichen, um Ihnen den Einstieg zu erleichtern.
quelle
In Anbetracht all der erwähnten Dinge sieht es so aus, als ob der Engpass der Join selbst ist.
Aspekt 1: Puffergröße verbinden
Höchstwahrscheinlich ist Ihre join_buffer_size zu niedrig.
Gemäß der MySQL-Dokumentation zur Verwendung des Join Buffer Cache durch MySQL
In diesem Fall bleiben die Schlüssel des Join-Puffers im RAM.
Sie haben 10 Millionen Zeilen mal 4 Bytes für jeden Schlüssel. Das sind ungefähr 40 Millionen.
Versuchen Sie es in der Sitzung auf 42 Millionen (etwas größer als 40 Millionen) zu erhöhen.
Wenn dies der Trick ist, fahren Sie fort, um dies hinzuzufügen
my.cnf
Ein Neustart von mysqld ist für neue Verbindungen nicht erforderlich. Renn einfach
ASPECT # 2: Join-Vorgang
Sie können den Stil der Verknüpfungsoperation ändern, indem Sie den Optimierer tweeten
Gemäß der MySQL-Dokumentation zum Blockieren von Nested-Loop- und Batched-Key-Access-Joins
Dieselbe Seite empfiehlt dies zu tun:
ASPECT # 3: Schreiben von Updates auf die Festplatte (OPTIONAL)
Die meisten vergessen , die zu erhöhen innodb_write_io_threads zu schreiben schmutzigen Seiten aus dem Pufferpool schneller.
Sie müssen MySQL für diese Änderung neu starten
VERSUCHE ES !!!
quelle
CREATE TABLE
das passt zur CSVLOAD DATA
in diesen TischUPDATE real_table JOIN csv_table ON ... SET ..., ..., ...;
DROP TABLE csv_table;
Schritt 3 ist viel schneller als zeilenweise, sperrt jedoch alle Zeilen in der Tabelle für einen nicht unerheblichen Zeitraum. Wenn diese Sperrzeit wichtiger ist als die Dauer des gesamten Vorgangs, ...
Wenn nichts anderes an den Tisch schreibt, dann ...
CREATE TABLE
das passt zur CSV; keine Indizes außer dem, was in derJOIN
in der benötigt wirdUPDATE
. Wenn es einzigartig ist, machen Sie esPRIMARY KEY
.LOAD DATA
in diesen Tischreal_table
nachnew_table
(CREATE ... SELECT
)UPDATE new_table JOIN csv_table ON ... SET ..., ..., ...;
RENAME TABLE real_table TO old, new_table TO real_table;
DROP TABLE csv_table, old;
Schritt 3 ist schneller als das Update, insbesondere wenn nicht benötigte Indizes weggelassen werden.
Schritt 5 ist "augenblicklich".
quelle
pt-online-schema-digest
; Es kümmert sich um solche Fragen über einTRIGGER
.LOAD DATA
. Das Hinzufügen unnötiger Indizes ist zeitaufwändig.AUTO_INCREMENT
Zeile in eine MyISAM-Tabelle zu laden und dann auf der Grundlage der PK jeweils 1 KB-Zeilen zu teilen. Aber ich muss alle Anforderungen und das Tabellenschema sehen, bevor ich versuchen kann, die Details zu formulieren.PRIMARY index
, aber während das Chunking in 50 KB mit der Bestellabfrage mehr Zeit in Anspruch nimmt. Wäre es besser, wenn ich ein automatisches Inkrement erstelle? und setze es alsPRIMARY index
?Du hast gesagt:
Viele dieser Aussagen können widersprüchlich sein. Zum Beispiel große Aktualisierungen ohne Sperren der Tabelle. Oder vermeiden Sie Rennbedingungen ohne eine einzige große Transaktion.
Da Ihre Tabelle stark indiziert ist, können sowohl Einfügungen als auch Aktualisierungen langsam sein.
Rennbedingungen vermeiden
Wenn Sie Ihrer Tabelle einen aktualisierten Zeitstempel hinzufügen können, können Sie die Rennbedingungen ermitteln und gleichzeitig vermeiden, eine halbe Million Aktualisierungen in einer einzigen Transaktion zu protokollieren.
Auf diese Weise können Sie zeilenweise Aktualisierungen durchführen (wie derzeit), jedoch mit automatischer Festschreibung oder sinnvolleren Transaktionsstapeln.
Sie vermeiden Rennbedingungen (während der zeilenweisen Aktualisierung), indem Sie prüfen, ob noch keine spätere Aktualisierung stattgefunden hat (
UPDATE ... WHERE pk = [pk] AND updated < [batchfile date]
).Auf diese Weise können Sie vor allem parallele Aktualisierungen durchführen.
So schnell wie möglich laufen - Parallelisieren
Mit diesem Zeitstempel prüfen Sie jetzt an Ort und Stelle:
mysql
jede SQL-Datei ausgeführt werden.(zB im
bash
Blick aufsplit
undxargs -P
nach Möglichkeiten , um einfach einen Befehl viele Möglichkeiten , parallel laufen. Grad der Parallelität hängt davon ab , wie viele Threads Sie bereit sind , zu dem widmen Update )quelle
Große Updates sind E / A-gebunden. Ich würde vorschlagen:
quelle
Damit das
UPDATE
schnell läuft, braucht manEs kann auf beiden Tischen stehen. Die drei Felder können in beliebiger Reihenfolge sein.
Dies erleichtert die
UPDATE
schnelle Zuordnung der Zeilen zwischen den beiden Tabellen.Und macht die Datentypen das gleiche in den beiden Tabellen (beide
INT SIGNED
oder beideINT UNSIGNED
).quelle
EXPLAIN UPDATE ...;
.