Wie man eine Spalte zu einer großen Tabelle in MySQL hinzufügt

12

Ich bin ein PHP-Entwickler, seien Sie also nicht streng. Ich habe einen großen Tisch ~ 5,5 GB Dump. Unser PM hat beschlossen, eine neue Spalte zu erstellen, um eine neue Funktion auszuführen. Tabelle ist InnoDB also was ich versucht habe:

  1. Ändern Sie die Tabelle auf dem Bildschirm mit der Tabellensperre. Dauerte ~ 30 Stunden und nichts. Also habe ich einfach aufgehört. Zuerst habe ich einen Fehler gemacht, weil ich nicht alle Transaktionen beendet habe, aber das zweite Mal war kein Multilock. Status war copy to tmp table.

  2. Da ich auch die Partitionierung für diese Tabelle anwenden muss, beschließen wir, einen Speicherauszug zu erstellen, die Tabelle umzubenennen und mit demselben Namen und neuer Struktur zu erstellen. Aber Dump macht eine strenge Kopie (zumindest habe ich nichts anderes gefunden). Also habe ich hinzugefügt, um eine neue Spalte mit sedauszugeben und abzufragen. Aber einige seltsame Fehler begannen. Ich glaube, es wurde durch Zeichensatz verursacht. Tabelle in utf-8 und Datei wurde nach us-ascii sed. Ich habe also Fehler (unbekannter Befehl '\' ') bei 30% der Daten erhalten. Das ist also auch ein schlechter Weg.

Was sind andere Optionen, um dies zu erreichen und die Leistung zu beschleunigen (ich kann es mit PHP-Skript tun, aber es wird ewig dauern). Was wird Leistung INSERT SELECTin diesem Fall sein.

Vielen Dank für jeden Fortschritt.

ineersa
quelle

Antworten:

11

Verwenden Sie MySQL Workbench . Sie können mit der rechten Maustaste auf eine Tabelle klicken und "An SQL Editor senden" -> "Anweisung erstellen" auswählen. Auf diese Weise wird nicht vergessen, Tabelleneigenschaften hinzuzufügen (einschließlich CHARSEToder COLLATE).
Bei dieser riesigen Datenmenge würde ich empfehlen, entweder die Tabelle oder die von Ihnen verwendete Datenstruktur zu bereinigen (ein guter DBA ist praktisch). Wenn nicht möglich:

  • Benennen Sie die Tabelle ( ALTER) um und erstellen Sie eine neue mit dem CREATESkript, das Sie von Workbench erhalten. Sie können diese Abfrage auch mit dem neuen Feld erweitern, das Sie benötigen
  • BULK LOAD die Daten aus der alten in die neue Tabelle:
    SET FOREIGN_KEY_CHECKS = 0;
    SET UNIQUE_CHECKS = 0;
    SET AUTOCOMMIT = 0;
    INSERT INTO new_table (fieldA, fieldB, fieldC, ..., fieldN)
       SELECT fieldA, fieldB, fieldC, ..., fieldN
       FROM old_table
    SET UNIQUE_CHECKS = 1;
    SET FOREIGN_KEY_CHECKS = 1;
    COMMIT;

    Auf diese Weise vermeiden Sie die Indizierung / etc, um Datensatz für Datensatz auszuführen. Die "Aktualisierung" der Tabelle wird immer noch langsam sein (da die Datenmenge sehr groß ist), aber dies ist der schnellste Weg, den ich mir vorstellen kann.

    BEARBEITEN: Lesen Sie diesen Artikel, um Details zu den Befehlen zu erhalten, die in der obigen Beispielabfrage verwendet werden.

quelle
Meine Optionen sind in Ordnung. Und ich habe SET NAMES utf8und COLLATION.Aber meh idk, warum 30% der Daten danach beschädigt sed. Ich denke, die Massenladung wird die schnellste sein, aber vielleicht gibt es noch etwas, das mir fehlt. Vielen Dank Mark
ineersa 18.06.13
1
Die Beschädigung von @ineersa-Daten kann viele Gründe haben: Sie haben die Datei beispielsweise mit einem Editor geöffnet, der nicht alle Zeichen unterstützt, und sie gespeichert. Oder die Art und Weise, wie Sie versuchen, Daten aus dem Speicherauszug zu importieren, beschädigt die Daten (sie sind fehlerhaft und können die Datei nicht richtig lesen). Oder derselbe Typ kann einen Teil einiger Daten als Ausdruck (z. B. "james \ robin" == "\ r" als Ausdruck) oder Befehl usw. identifizieren. Aus diesem Grund empfehle ich niemals, dump zu verwenden, auch nicht mit dem Dump-Tool für binäre Daten nur, nicht einmal mit dev.mysql.com/doc/refman/5.6/en/mysqldump.html (oder BCP für MS SQL Server). Es geht viel zu oft schief ...
Ja, ich habe es mit Hex-Blob versucht. es hilft nicht. Auch Sie direkt nach der Verwendung von sed mysql identifizieren \ 'als Befehl in einigen Namen (nicht in allen). Das ist seltsam und fehlerhaft. Werde heute Abend eine Massenladung versuchen. Hoffe, es wird spätestens in 10-15 Stunden erledigt.
Ineersa
@ineersa hoffe es wird. Sie können auch versuchen, nur einen Teil der Daten hinzuzufügen. Nehmen wir beispielsweise 10% der Daten an, um zu sehen, wie lange dies dauert - und um eine Schätzung für die gesamte Transaktion zu erhalten. Es wird jedoch eine sehr grobe Schätzung sein, die Dinge können sich verlangsamen, wenn Caches / Speicher / alles, was voll / überladen ist.
1
Danke Markus. Hat super funktioniert. Noch schneller als die Wiederherstellung von Dump. Dauerte ~ 5 Stunden.
Ineersa
5

Ihre sed Idee ist eine anständige Methode, aber ohne die Fehler oder den Befehl, den Sie ausgeführt haben, können wir Ihnen nicht helfen.

Eine bekannte Methode für Online-Änderungen an großen Tabellen ist jedoch die Änderung des pt-online-Schemas . Das vereinfachte Übersehen der Funktionen dieses Tools wird aus der Dokumentation kopiert:

pt-online-schema-change erstellt eine leere Kopie der zu ändernden Tabelle, ändert sie nach Bedarf und kopiert dann Zeilen aus der ursprünglichen Tabelle in die neue Tabelle. Wenn der Kopiervorgang abgeschlossen ist, wird die ursprüngliche Tabelle entfernt und durch die neue ersetzt. Standardmäßig wird auch die Originaltabelle gelöscht.

Diese Methode kann auch eine Weile dauern, aber während des Vorgangs kann die ursprüngliche Tabelle vollständig verwendet werden.

Derek Downey
quelle
Ich werde versuchen, später am Abend loszulegen. Wenn es nicht funktioniert, wird dieses Tool wahrscheinlich benötigt. Fehler werden dadurch verursacht, dass einige Symbole nach der Verwendung von sed als Befehle nicht mehr erkannt werden. Zum Beispiel 'D\'agostini'wird ein Fehler verursacht unknown command '\''. Aber nicht immer, wie in 30% der Fälle. Das ist seltsam und fehlerhaft. Gleiches gilt auch für Hex-Blob-Dumps. Vielen Dank, Derek.
Ineersa
4

alter table add column, algorithm=inplace, lock=none ändert eine MySQL 5.6-Tabelle, ohne die Tabelle zu kopieren und ohne Auswirkungen zu sperren.

Gerade gestern getestet, fügte die Masse 70.000 Zeilen in eine Partitionstabelle mit 280.000 Zeilen und 7 Partitionen ein, 10.000 Zeilen in jede Partition, wobei 5 Sekunden dazwischen geschlafen wurden, um anderen Durchsatz zu ermöglichen.

Begann die Masseneinfügungen, dann startete in einer separaten Sitzung die alterobige Online- Anweisung in MySQL Workbench, die alterbeendet wurde, bevor die Einfügungen, zwei neue Spalten hinzugefügt wurden und keine Zeilen aus der Änderung resultierten, was bedeutet, dass MySQL keine Zeilen kopierte.

SAK
quelle
1
Warum bekommt diese Antwort nicht mehr Stimmen? Funktioniert sie nicht?
Fguillen
1

Derzeit ist die beste Option zum Ändern großer Tabellen wahrscheinlich https://github.com/github/gh-ost

gh-ost ist eine Online-Schema-Migrationslösung ohne Trigger für MySQL. Es ist testbar und bietet Pausierbarkeit, dynamische Steuerung / Rekonfiguration, Überwachung und viele betriebliche Vorteile.

gh-ost erzeugt während der Migration eine leichte Arbeitslast auf dem Master, die von der vorhandenen Arbeitslast auf der migrierten Tabelle entkoppelt ist.

Es basiert auf jahrelanger Erfahrung mit vorhandenen Lösungen und ändert das Paradigma der Tabellenmigrationen.

iJanki
quelle
1

Ich denke, Mydumper / Myloader ist ein gutes Werkzeug für Operationen wie diese: Wird von Tag zu Tag besser. Sie können Ihre CPUs nutzen und Daten parallel laden: http://www.percona.com/blog/2014/03/10/new-mydumper-0-6-1-release-offers-several-performance-and- Usability-Features /

Ich habe es geschafft, Hunderte Gigabyte MySQL-Tabellen in Stunden zu laden.

Nun, wenn es um das Hinzufügen eine neue Spalte kommt, ist schwierig , da MySQL kopiert die gesamte Tabelle in dem Speicher - TMPBereich mit ALTER TABLE...Obwohl MySQL 5.6 sagt es online Schemaänderungen tun, habe ich nicht geschaffen , sie online massive Tabellen ohne Sperre zu tun Streit noch.

Kubilay
quelle
-2

Ich hatte nur das gleiche Problem. Ein kleiner Workaround:

CREATE TABLE new_table SELECT * FROM oldtable;

DELETE FROM new_table

ALTER TABLE new_table ADD COLUMN new_column int (11);

INSERT INTO new_table wähle *, 0 aus old_table

drop table old_table; benenne die Tabelle new_table um TO old_table;

AirCoder
quelle
Warum nicht einfach eine where-Klausel zur create table-Anweisung hinzufügen, damit keine Daten ausgewählt werden? Auch das Abschneiden der Tabelle wäre effizienter als das Löschen der Daten
Joe W
warum löschen, wenn später nochmal einfügen müssen. Kann bei ADD COLUMN selbst default = 0 definieren.
user195280