InnoDB-Importleistung

10

Ich habe Probleme mit dem Massenimport einer ziemlich großen InnoDB-Tabelle, die aus ungefähr 10 Millionen Zeilen (oder 7 GB) besteht (was für mich die größte Tabelle ist, mit der ich bisher gearbeitet habe).

Ich habe einige Nachforschungen angestellt, wie die Importgeschwindigkeit von Inno verbessert werden kann, und im Moment sieht mein Setup folgendermaßen aus:

/etc/mysql/my.cnf/
[...]
innodb_buffer_pool_size = 7446915072 # ~90% of memory
innodb_read_io_threads = 64
innodb_write_io_threads = 64
innodb_io_capacity = 5000
innodb_thread_concurrency=0
innodb_doublewrite = 0
innodb_log_file_size = 1G
log-bin = ""
innodb_autoinc_lock_mode = 2
innodb_flush_method = O_DIRECT
innodb_flush_log_at_trx_commit=2
innodb_buffer_pool_instances=8


import is done via bash script, here is the mysql code:
SET GLOBAL sync_binlog = 1;
SET sql_log_bin = 0;
SET FOREIGN_KEY_CHECKS = 0;
SET UNIQUE_CHECKS = 0;
SET AUTOCOMMIT = 0;
SET SESSION tx_isolation='READ-UNCOMMITTED';
LOAD DATA LOCAL INFILE '$filepath' INTO TABLE monster
COMMIT;

Daten werden in einer CSVDatei bereitgestellt .
Derzeit teste ich meine Einstellungen mit kleineren 'Test-Dumps' mit jeweils 2 Millionen, 3 Millionen, ... Zeilen und time import_script.shvergleiche die Leistung.

Nachteil ist, dass ich nur eine Gesamtlaufzeit bekomme, also muss ich warten, bis der vollständige Import abgeschlossen ist, um ein Ergebnis zu erhalten.

Meine bisherigen Ergebnisse:

  • 10 000 Zeilen: <1 Sekunde
  • 100 000 Zeilen: 10 Sekunden
  • 300 000 Zeilen: 40 Sekunden
  • 2 Millionen Zeilen: 18 Minuten
  • 3 Millionen Zeilen: 26 Minuten
  • 4 Millionen Zeilen: (nach 2 Stunden storniert)

Es scheint, dass es keine "Kochbuch" -Lösung gibt und man die optimale Mischung der Einstellungen selbst herausfinden muss.
Neben Vorschlägen, was in meinem Setup geändert werden soll, würde ich mich auch über weitere Informationen freuen, wie ich den Importprozess besser bewerten / mehr Einblicke gewinnen könnte, was passiert und wo der Engpass liegen könnte.
Ich habe versucht, die Dokumentation zu den Einstellungen zu lesen, die ich ändere, aber andererseits sind mir keine Nebenwirkungen bekannt und ob ich die Leistung mit einem schlecht gewählten Wert sogar verringern könnte.

Im Moment möchte ich einen Vorschlag aus dem Chat ausprobieren, der MyISAMbeim Import verwendet werden soll, und anschließend die Tabellen-Engine ändern.
Ich würde es gerne versuchen, aber im Moment DROP TABLEdauert es auch Stunden, bis meine Abfrage abgeschlossen ist. (Was ein weiterer Indikator zu sein scheint, ist meine Einstellung weniger als optimal).

Zusätzliche Informationen:
Der Computer, den ich derzeit verwende, verfügt über 8 GB RAM und eine Solid State Hybrid-Festplatte mit 5400 U / min.
Während wir auch versuchen, veraltete Daten aus der fraglichen Tabelle zu entfernen, brauche ich noch einen etwas schnellen Import, um
a) automatic data cleanup featurewährend der Entwicklung zu testen und
b) falls unser Server abstürzt, möchten wir unseren 2. Server als Ersatz verwenden (der benötigt wird) - Bisherige Daten, letzter Import dauerte mehr als 24 Stunden)

mysql> SHOW CREATE TABLE monster\G
*************************** 1. row ***************************
       Table: monster
Create Table: CREATE TABLE `monster` (
  `monster_id` int(11) NOT NULL AUTO_INCREMENT,
  `ext_monster_id` int(11) NOT NULL DEFAULT '0',
  `some_id` int(11) NOT NULL DEFAULT '0',
  `email` varchar(250) NOT NULL,
  `name` varchar(100) NOT NULL,
  `address` varchar(100) NOT NULL,
  `postcode` varchar(20) NOT NULL,
  `city` varchar(100) NOT NULL,
  `country` int(11) NOT NULL DEFAULT '0',
  `address_hash` varchar(250) NOT NULL,
  `lon` float(10,6) NOT NULL,
  `lat` float(10,6) NOT NULL,
  `ip_address` varchar(40) NOT NULL,
  `cookie` int(11) NOT NULL DEFAULT '0',
  `party_id` int(11) NOT NULL,
  `status` int(11) NOT NULL DEFAULT '2',
  `creation_date` datetime NOT NULL,
  `someflag` tinyint(1) NOT NULL DEFAULT '0',
  `someflag2` tinyint(4) NOT NULL,
  `upload_id` int(11) NOT NULL DEFAULT '0',
  `news1` tinyint(4) NOT NULL DEFAULT '0',
  `news2` tinyint(4) NOT NULL,
  `someother_id` int(11) NOT NULL DEFAULT '0',
  `note` varchar(2500) NOT NULL,
  `referer` text NOT NULL,
  `subscription` int(11) DEFAULT '0',
  `hash` varchar(32) DEFAULT NULL,
  `thumbs1` int(11) NOT NULL DEFAULT '0',
  `thumbs2` int(11) NOT NULL DEFAULT '0',
  `thumbs3` int(11) NOT NULL DEFAULT '0',
  `neighbours` tinyint(4) NOT NULL DEFAULT '0',
  `relevance` int(11) NOT NULL,
  PRIMARY KEY (`monster_id`),
  KEY `party_id` (`party_id`),
  KEY `creation_date` (`creation_date`),
  KEY `email` (`email`(4)),
  KEY `hash` (`hash`(8)),
  KEY `address_hash` (`address_hash`(8)),
  KEY `thumbs3` (`thumbs3`),
  KEY `ext_monster_id` (`ext_monster_id`),
  KEY `status` (`status`),
  KEY `note` (`note`(4)),
  KEY `postcode` (`postcode`),
  KEY `some_id` (`some_id`),
  KEY `cookie` (`cookie`),
  KEY `party_id_2` (`party_id`,`status`)
) ENGINE=InnoDB AUTO_INCREMENT=13763891 DEFAULT CHARSET=utf8
nuala
quelle
2
Haben Sie es mit weniger großen Importen wie 10.000 oder 100.000 Zeilen versucht?
Ypercubeᵀᴹ
1
Bitte führen Sie SHOW CREATE TABLE yourtable\Guns aus, um uns die Tabellenstruktur dieser 10-Millionen- Zeilentabelle zu zeigen.
RolandoMySQLDBA
@ RolandoMySQLDBA so tat ich (mit verdeckten Feldnamen)
nuala
Durch Deaktivieren des doppelten Schreibpuffers ( innodb_doublewrite = 0) ist Ihre MySQL-Installation nicht absturzsicher: Wenn Sie einen Stromausfall haben (kein MySQL-Absturz), werden Ihre Daten möglicherweise unbemerkt beschädigt.
jfg956

Antworten:

13

Zunächst müssen Sie wissen, was Sie mit InnoDB tun, wenn Sie Millionen von Zeilen in eine InnoDB-Tabelle pflügen. Werfen wir einen Blick auf die InnoDB-Architektur.

InnoDB-Architektur

In der oberen linken Ecke befindet sich eine Abbildung des InnoDB-Pufferpools. Beachten Sie, dass es einen Abschnitt gibt, der dem Einfügepuffer gewidmet ist. Was macht das? Es ist vorgesehen, Änderungen an Sekundärindizes aus dem Pufferpool in den Einfügepuffer im Systemtabellenbereich (auch bekannt als ibdata1) zu migrieren. Standardmäßig ist innodb_change_buffer_max_size auf 25 festgelegt. Dies bedeutet, dass bis zu 25% des Pufferpools für die Verarbeitung von Sekundärindizes verwendet werden können.

In Ihrem Fall haben Sie 6,935 GB für den InnoDB-Pufferpool. Für die Verarbeitung Ihrer Sekundärindizes werden maximal 1,734 GB verwendet.

Schauen Sie sich jetzt Ihren Tisch an. Sie haben 13 Sekundärindizes. Jede Zeile, die Sie verarbeiten, muss einen sekundären Indexeintrag generieren, ihn mit dem Primärschlüssel der Zeile koppeln und als Paar vom Einfügepuffer im Pufferpool in den Einfügepuffer in ibdata1 senden. Das passiert 13 mal mit jeder Zeile. Multiplizieren Sie dies mit 10 Millionen und Sie können fast einen Engpass spüren.

Vergessen Sie nicht, dass beim Importieren von 10 Millionen Zeilen in einer einzelnen Transaktion alles in einem Rollback-Segment zusammengefasst und der UNDO-Speicherplatz in ibdata1 ausgefüllt wird.

VORSCHLÄGE

VORSCHLAG # 1

Mein erster Vorschlag zum Importieren dieser ziemlich großen Tabelle wäre

  • Löschen Sie alle nicht eindeutigen Indizes
  • Importieren Sie die Daten
  • Erstellen Sie alle nicht eindeutigen Indizes

VORSCHLAG # 2

Entfernen Sie doppelte Indizes. In Ihrem Fall haben Sie

KEY `party_id` (`party_id`),
KEY `party_id_2` (`party_id`,`status`)

Beide Indizes beginnen mit party_id. Sie können die Verarbeitung des Sekundärindex um mindestens 7,6% erhöhen, um einen von 13 Indizes zu entfernen. Sie müssen schließlich ausgeführt werden

ALTER TABLE monster DROP INDEX party_id;

VORSCHLAG # 3

Entfernen Sie nicht verwendete Indizes. Überprüfen Sie Ihren Anwendungscode und prüfen Sie, ob Ihre Abfragen alle Indizes verwenden. Möglicherweise möchten Sie sich mit der Verwendung von pt-index befassen, um darauf hinzuweisen, welche Indizes nicht verwendet werden.

VORSCHLAG # 4

Sie sollten die Größe von innodb_log_buffer_size auf 64 MB erhöhen, da der Standardwert 8 MB beträgt. Ein größerer Protokollpuffer kann die InnoDB-Schreib-E / A-Leistung erhöhen.

EPILOG

Gehen Sie wie folgt vor, um die ersten beiden Vorschläge umzusetzen:

  • Löschen Sie die 13 nicht eindeutigen Indizes
  • Importieren Sie die Daten
  • Erstellen Sie alle nicht eindeutigen Indizes mit Ausnahme des party_idIndex

Vielleicht kann das Folgende helfen

CREATE TABLE monster_new LIKE monster;
ALTER TABLE monster_new
  DROP INDEX `party_id`,
  DROP INDEX `creation_date`,
  DROP INDEX `email`,
  DROP INDEX `hash`,
  DROP INDEX `address_hash`,
  DROP INDEX `thumbs3`,
  DROP INDEX `ext_monster_id`,
  DROP INDEX `status`,
  DROP INDEX `note`,
  DROP INDEX `postcode`,
  DROP INDEX `some_id`,
  DROP INDEX `cookie`,
  DROP INDEX `party_id_2`;
ALTER TABLE monster RENAME monster_old;
ALTER TABLE monster_new RENAME monster;

Importieren Sie die Daten in monster. Führen Sie dann dies aus

ALTER TABLE monster
  ADD INDEX `creation_date`,
  ADD INDEX `email` (`email`(4)),
  ADD INDEX `hash` (`hash`(8)),
  ADD INDEX `address_hash` (`address_hash`(8)),
  ADD INDEX `thumbs3` (`thumbs3`),
  ADD INDEX `ext_monster_id` (`ext_monster_id`),
  ADD INDEX `status` (`status`),
  ADD INDEX `note` (`note`(4)),
  ADD INDEX `postcode` (`postcode`),
  ADD INDEX `some_id` (`some_id`),
  ADD INDEX `cookie` (`cookie`),
  ADD INDEX `party_id_2` (`party_id`,`status`);

VERSUCHE ES !!!

ALTERNATIVE

Sie können eine Tabelle erstellen, die monster_csvals MyISAM-Tabelle ohne Indizes bezeichnet wird, und dies tun:

CREATE TABLE monster_csv ENGINE=MyISAM AS SELECT * FROM monster WHERE 1=2;
ALTER TABLE monster RENAME monster_old;
CREATE TABLE monster LIKE monster_old;
ALTER TABLE monster DROP INDEX `party_id`;

Importieren Sie Ihre Daten in monster_csv. Verwenden Sie dann mysqldump, um einen weiteren Import zu erstellen

mysqldump -t -uroot -p mydb monster_csv | sed 's/monster_csv/monster/g' > data.sql

Die mysqldump-Datei data.sqlerweitert INSERT-Befehle, die jeweils 10.000 bis 20.000 Zeilen importieren.

Laden Sie jetzt einfach den mysqldump

mysql -uroot -p mydb < data.sql

Entfernen Sie zum Schluss die MyISAM-Tabelle

DROP TABLE monster_csv;
RolandoMySQLDBA
quelle
Ich war mir nicht einmal all dieser Schlüssel bewusst (es ist nicht mein Design), aber Ihre Erklärung scheint sehr überzeugend. Für heute ist es zu spät, um einen weiteren Versuch zu starten, aber ich sehe einige gute Ratschläge, was ich morgen ausprobieren sollte. Werde Sie auf dem Laufenden halten! <3
Nuala
1
Ich habe es geschafft, die vollständige Datenbank (nicht nur die monsterTabelle) in weniger als 20 Minuten zu importieren, wenn keine Schlüssel für InnoDB-Tabellen vorhanden waren. Das Hinzufügen von Schlüsseln dauerte ca. weitere 20 min. Ich würde sagen, das löst mein Problem in diesem Fall ziemlich genau. Vielen Dank!
Nuala
8

Ich wollte einen Kommentar schreiben (da dies keine endgültige Antwort ist), aber es wurde zu lang:

Ich werde Ihnen einige allgemeine Ratschläge geben, und wir können auf jeden einzelnen eingehen, wenn Sie möchten:

  • Reduzieren Sie die Haltbarkeit (Sie haben bereits einige davon durchgeführt). Neueste Versionen ermöglichen es sogar, mehr zu tun. Sie können den doppelten Schreibpuffer sogar deaktivieren, da Beschädigungen beim Import kein Problem darstellen.
  • Erhöhen Sie die Pufferung um: Erhöhen Sie die Größe des Transaktionsprotokolls und erhöhen Sie die Größe des verfügbaren Pufferpools. Überwachen Sie die Verwendung der Transaktionsprotokolldatei und die Prüfpunkte. Fürchte dich nicht vor riesigen Protokollen für einen Import.
  • Vermeiden Sie große Transaktionen - Ihr Rollback wird mit nicht benötigten Daten gefüllt. Dies ist wahrscheinlich Ihr größtes Problem.
  • SQL ist ein Engpass. Vermeiden Sie den SQL-Overhead (Handlersocket, Memcached) und / oder laden Sie ihn gleichzeitig mit mehreren Threads. Parallelität muss einen Sweet Spot erreichen, nicht zu viel, nicht zu wenig.
  • Das Laden von Daten in Fragmentierung der Primärschlüsselreihenfolge kann eine isse sein
  • Testen Sie die InnoDB-Komprimierung, wenn E / A Ihr Engpass ist und CPU und Speicher sie nicht langsamer machen
  • Versuchen Sie anschließend, Ihre Sekundärschlüssel zu erstellen (in einigen Fällen schneller). Laden Sie keine indizierten Daten. DISABLE KEYS wirkt sich nicht auf InnoDB aus . Wenn nicht, überwachen Sie Ihren Einfügepuffer (möglicherweise überholen Sie die Hälfte Ihres Pufferpools).
  • Ändern oder Deaktivieren des Prüfsummenalgorithmus - wahrscheinlich nicht Ihr Problem, aber es wird zu einem Engpass bei High-End-Flashkarten.
  • Letzter Ausweg: Überwachen Sie Ihren Server, um Ihren aktuellen Engpass zu finden, und versuchen Sie, ihn zu verringern (InnoDB ist diesbezüglich sehr flexibel).

Denken Sie daran, dass einige davon für Nichtimporte nicht sicher oder ratsam sind (normaler Betrieb).

Jynus
quelle
Vielen Dank! Ich probiere gerne zuerst Rolandos Idee bezüglich Indizes aus, aber ich denke, dass dieses "Transaktions-Rollback" -Ding immer noch ein Problem sein wird. Könnten Sie das näher erläutern? Ich denke, ich möchte so viel wie möglich von dieser Funktionalität während des Imports deaktivieren und sie einfach wieder aktivieren, wenn ich in die Produktion gehe ~ Ich denke ...
nuala
1
Rolandos Vorschlag ist mein Punkt # 7. Das Vermeiden von Rollback-Overhead ist so einfach wie eine Kombination aus SET SESSION tx_isolation='READ-UNCOMMITTED';(nur nützlich, wenn Sie mit mehreren Threads gleichzeitig importieren) und einem @ ypercube-Kommentar zum Einfügen in Stapel. Ein vollständiges Beispiel finden Sie hier: mysqlperformanceblog.com/2008/07/03/… Stellen Sie sicher, dass Sie alle Funktionen der neuesten InnoDB-Versionen nutzen: mysqlperformanceblog.com/2011/01/07/…
jynus
1
Ich hatte den allgemeinen Eindruck, man würde es vermeiden, in kleinere Spannfutter zu importieren, sondern sich für eine "All-Inclusive" -Operation entscheiden, aber ich sehe, dass Multithreading einige Möglichkeiten eröffnen könnte. Ich denke, das ist sehr fallspezifisch. Ich akzeptierte jedoch Rolandos Antwort, da diese Optimierung (Ihre Nr. 7) mir allein geholfen hat, in <1 Stunde den vollständigen Import zu erreichen, aber Ihre Liste ist definitiv alles andere als wertlos und ich denke, sie wird sie als Referenz verwenden, sobald die Rate, mit der unsere DB wächst, irgendwie wächst macht mir Angst :)
Nuala
Ich stimme @yoshi zu. Ihre Antwort ist umfassender in Bezug auf Fehlerbehebung und Leistungsverbesserungen. +1
RolandoMySQLDBA
3

Die meisten guten Tipps wurden bisher gegeben, aber ohne viele Erklärungen für die besten. Ich werde mehr Details geben.

Erstens ist es gut, die Indexerstellung zu verzögern, da in anderen Antworten genügend Details enthalten sind. Ich werde nicht darauf zurückkommen.

Eine größere InnoDB-Protokolldatei hilft Ihnen sehr (wenn Sie MySQL 5.6 verwenden, da es in MySQL 5.5 nicht möglich ist, sie zu erhöhen). Wenn Sie 7 GB Daten einfügen, würde ich eine Gesamtprotokollgröße von mindestens 8 GB empfehlen (beibehalten Sie innodb_log_files_in_groupdie Standardeinstellung (2) und erhöhen Sie die Größe innodb_log_file_sizeauf 4 GB). Diese 8 GB sind nicht genau: Sie sollten mindestens die Importgröße im REDO-Protokoll haben und diese Größe wahrscheinlich verdoppeln oder vervierfachen. Die Gründe für die Größe des InnoDB-Protokolls erhöhen, dass InnoDB, wenn das Protokoll fast voll wird, seinen Pufferpool aggressiv auf die Festplatte spült, um ein Auffüllen des Protokolls zu vermeiden (wenn das Protokoll voll ist, kann InnoDB erst nach einiger Zeit Datenbank schreiben Seiten des Pufferpools werden auf die Festplatte geschrieben.

Eine größere InnoDB-Protokolldatei hilft Ihnen, aber Sie sollten sie auch in der Reihenfolge des Primärschlüssels einfügen (sortieren Sie Ihre Datei vor dem Einfügen). Wenn Sie in Primärschlüsselreihenfolge einfügen, füllt InnoDB eine Seite und dann eine weitere und so weiter. Wenn Sie nicht in der Reihenfolge des Primärschlüssels einfügen, wird Ihre nächste Einfügung möglicherweise auf einer Seite angezeigt, die voll ist und einen "Seitenteilung" verursacht. Diese Seitenaufteilung ist für InnoDB teuer und verlangsamt Ihren Import.

Sie haben bereits einen Pufferpool, der so groß ist, wie es Ihr RAM zulässt, und wenn Ihre Tabelle nicht hineinpasst, können Sie nur viel RAM kaufen. Wenn Ihre Tabelle in den Pufferpool passt, aber größer als 75% Ihres Pufferpools ist, können Sie versuchen innodb_max_dirty_pages_pct, sie während des Imports auf 85 oder 95 zu erhöhen (der Standardwert ist 75). Dieser Konfigurationsparameter weist InnoDB an, den Pufferpool aggressiv zu leeren, wenn der Prozentsatz der verschmutzten Seiten diese Grenze erreicht. Wenn Sie diesen Parameter erhöhen (und wenn Sie Glück mit der Datengröße haben), können Sie aggressive E / A während des Imports vermeiden und diese E / A auf später verschieben.

Vielleicht (und das ist eine Vermutung) hilft Ihnen das Importieren Ihrer Daten in viele kleine Transaktionen. Ich weiß nicht genau, wie das REDO-Protokoll erstellt wird, aber wenn es im RAM gepuffert wird (und auf der Festplatte, wenn zu viel RAM benötigt wird), während die Transaktion voranschreitet, können unnötige E / A-Vorgänge auftreten. Sie können dies versuchen: Sobald Ihre Datei sortiert ist, teilen Sie sie in viele Teile auf (versuchen Sie es mit 16 MB und anderen Größen) und importieren Sie sie nacheinander. Auf diese Weise können Sie auch den Fortschritt Ihres Imports steuern. Wenn Sie nicht möchten, dass Ihre Daten während des Imports für andere Leser teilweise sichtbar sind, können Sie sie unter einem anderen Tabellennamen importieren, die Indizes später erstellen und die Tabelle dann umbenennen.

Über Ihre Hybrid-SSD / 5400RPM-Festplatte weiß ich nichts darüber und wie ich dies optimieren kann. 5400RPM sieht für eine Datenbank langsam aus, aber möglicherweise vermeidet die SSD dies. Vielleicht füllen Sie den SSD-Teil Ihrer Festplatte mit sequentiellen Schreibvorgängen in das REDO-Protokoll, und die SSD beeinträchtigt die Leistung. Ich weiß nicht.

Ein schlechter Tipp, den Sie nicht ausprobieren sollten (oder mit dem Sie vorsichtig sein sollten), ist folgender: Verwenden Sie kein Multithread: Es ist sehr schwer zu optimieren, um Seitenaufteilungen in InnoDB zu vermeiden. Wenn Sie Multithread verwenden möchten, fügen Sie diese in verschiedene Tabellen (oder in verschiedene Partitionen derselben Tabelle) ein.

Wenn Sie Multi-Thread in Betracht ziehen, haben Sie möglicherweise einen NUMA-Computer (Multi-Socket). Stellen Sie in diesem Fall sicher, dass Sie das Problem des MySQL-Swap-Wahnsinns vermeiden .

Wenn Sie MySQL 5.5 verwenden, aktualisieren Sie auf MySQL 5.6: Es bietet die Möglichkeit, die REDO-Protokollgröße zu erhöhen, und verfügt über bessere Algorithmen zum Löschen des Pufferpools.

Viel Glück beim Import.

jfg956
quelle