Wie konvertiert man eine 66.862.521-Zeilentabelle von MyISAM nach InnoDB, ohne mehrere Stunden offline zu gehen?

18

Ist es möglich (und wie), eine große MyISAM-Tabelle in InnoDB zu konvertieren, ohne die Anwendung offline zu schalten? Sie müssen pro Sekunde ein paar Zeilen in diese Tabelle einfügen, können sie jedoch für ca. 2 Minuten anhalten.

Offensichtlich wird ALTER TABLE ... engine = innodb nicht funktionieren. Daher hatte ich den Plan, eine neue Tabelle mit der innodb-Engine zu erstellen und den Inhalt in diese zu kopieren. Und am Ende setzen Sie den Anwendungsprotokoll-Thread und RENAME TABLE aus.

Leider führt selbst das Kopieren in kleinen Stapeln von 100 Zeilen nach einiger Zeit zu einer erheblichen Verzögerung.

Bearbeiten : Bestehende Zeilen werden nie geändert, diese Tabelle wird für die Protokollierung verwendet.

Hendrik Brummermann
quelle
3
Bei dieser Frage geht es darum, die Gesprächszeit zu minimieren. Es ist mir egal, ob die Gespräche einige Tage oder Wochen dauern. Es muss jedoch im Hintergrund funktionieren, ohne dass die Anwendung heruntergefahren werden muss und ohne dass eine spürbare Verzögerung auftritt.
Hendrik Brummermann

Antworten:

15

Erstellen Sie ein Master-Master-Setup wie folgt:

  • Erstellen Sie den zweiten Master, MasterB
  • MasterB fungiert als Slave von logTable
  • logTable_newAls innodb erstellen
  • Führen Sie INSERT INTO logTable_new SELECT * FROM logTable(psuedocode) auf MasterB aus, wodurch die Replikation an MasterA gesendet wird
  • Wenn logTable_newMasterA die Synchronisierung abgeschlossen hat, tauschen Sie die Tabellen aus
Derek Downey
quelle
10

Angesichts der Einschränkung von:

Es ist mir egal, ob die Gespräche einige Tage oder Wochen dauern. Es muss jedoch im Hintergrund funktionieren, ohne dass die Anwendung heruntergefahren werden muss und ohne dass eine spürbare Verzögerung auftritt

Wenn Sie während der Protokollierung eine gute Möglichkeit haben, eine Markierung zu setzen, damit Sie feststellen können, wann Sie den Vorgang starten, können Sie Protokolle erneut anwenden oder die Protokolle in eine Textdatei schreiben Sie können sie später mit einnehmen LOAD DATA INFILE

Ein Teil des Problems besteht darin, dass das Schreiben in kleineren Stapeln bedeutet, dass die Indizes immer wieder neu berechnet werden müssen. Es ist besser, wenn Sie alles auf einmal ausführen. Dies kann jedoch zu einer spürbaren Verzögerung des Systems führen. Sie müssen dies jedoch nicht auf Ihrem Produktionsserver tun.

  1. Unterbrechen Sie die Protokollierung oder setzen Sie eine Markierung, damit Sie die Protokolle später erneut anwenden können.
  2. Kopieren Sie Ihre MyISM-Tabelle auf ein anderes System
  3. Erstellen Sie auf dem anderen System eine InnoDB-Tabelle unter einem anderen Namen und migrieren Sie die Daten (möglicherweise ist es sogar schneller, sie zu sichern und zu verwenden LOAD DATA INFILE).
  4. Kopieren Sie die InnoDB-Tabelle zurück in das ursprüngliche System
  5. Setzen Sie einen weiteren Marker für die Protokollierung.
  6. Wenden Sie alle Protokolle zwischen den letzten beiden Markierungen erneut auf die neue Tabelle an.
  7. (Wiederholen Sie die Schritte 5 und 6, wenn Schritt 6 länger als eine Minute gedauert hat, bis es nur noch wenige Sekunden sind.)
  8. Tauschen Sie die Tabellen aus (benennen Sie die alte in table_BACKUP um, die neue unter dem Namen der alten)
  9. Sammeln Sie die Protokolle seit dem letzten Marker.
Joe
quelle
9

Leider führt selbst das Kopieren in kleinen Stapeln von 100 Zeilen nach einiger Zeit zu einer erheblichen Verzögerung.

Fügen Sie eine Verzögerung zwischen den einzelnen Stapeln hinzu, oder führen Sie die Aktualisierungen nur stapelweise aus und führen Sie jeden Stapel direkt nach dem vorherigen aus?

Wenn ja, dann schreiben Sie die Konvertierung in Ihre Lieblingssprache mit etwas wie:

repeat
    copy oldest 100 rows that haven't been copied yet to new table
    sleep for as long as that update took
until there are <100 rows unprocessed
stop logging service
move the last few rows
rename tables
restart logging
delete the old table when you are sure the conversion has worked

Dies sollte sicherstellen, dass die Konvertierung nicht mehr als die Hälfte der Serverkapazität beansprucht, selbst wenn Unterschiede in der Auslastung berücksichtigt werden, da die Systemnutzung mit der Zeit variiert.

Oder wenn Sie so viel Zeit wie möglich verwenden möchten, während der Dienst relativ inaktiv ist, sich aber zurückzieht (möglicherweise für längere Zeit pausiert), wenn die Datenbank für ihre Benutzer etwas Arbeit erledigen muss, ersetzen Sie sleep for as long as the update tookdurch if the server's load is above <upper measure>, sleep for some seconds then check again, loop around the sleep/check until the load drops below <lower measure>. Dies bedeutet, dass es in ruhigen Zeiten vorwärtsdampfen kann, aber vollständig pausiert, wenn der Server mit seiner normalen Arbeitsauslastung beschäftigt ist. Die Bestimmung der Last hängt von Ihrem Betriebssystem ab - unter Linux und ähnlichen Bedingungen sollte der 1-Minuten-Durchschnittswert für die Last von /proc/loadavgoder die Ausgabe von uptimeausreichen. <lower measure>und <upper measure>kann derselbe Wert sein, obwohl es in Steuerelementen wie diesem üblich ist, einen Unterschied zu haben, damit Ihr Prozess nicht weiter startet und dann sofort pausiert, da sein eigener Neustart einen Einfluss auf das Lastmaß hat.

Dies funktioniert natürlich nicht für Tabellen, in denen alte Zeilen möglicherweise geändert werden, aber für eine Protokolltabelle wie die von Ihnen beschriebene.

In diesem Fall sollten Sie die übliche Vorgehensweise beim Erstellen von Indizes ignorieren, nachdem Sie die neue Tabelle ausgefüllt haben. Das ist in der Tat effizienter, wenn Sie möchten, dass die Dinge so schnell wie möglich ablaufen (was sich verdammt auf den Rest des Systems auswirkt), aber in diesem Fall möchten Sie nicht, dass am Ende des Prozesses eine große Lastschwemme entsteht wie bei Indizes werden vollständig auf einmal erstellt, da dies ein Vorgang ist, den Sie nicht unterbrechen können, wenn etwas los ist.

David Spillett
quelle
4

Würde so etwas funktionieren?

  1. Unterbrechen Sie die Protokollierung (damit sich das $auto_incrementin Ihrer Protokollierungstabelle mytable nicht ändert).
  2. Notieren Sie den $auto_incrementWert mit SHOW TABLE STATUS LIKE 'mytable'.
  3. CREATE TABLE mytable_new LIKE mytable
  4. ALTER TABLE mytable_new AUTO_INCREMENT=$auto_increment ENGINE=Innodb
  5. RENAME TABLE mytable TO mytable_old, mytable_new TO mytable
  6. Aktivieren Sie die Protokollierung erneut. Die Innodb-Tabelle wird nun mit Daten gefüllt.
  7. INSERT INTO mytable SELECT * FROM mytable_old.

Sie können Schritt 7 in Stapeln oder in einer Anweisung ausführen, da die normale Protokollierung nicht blockiert werden sollte.

Riedsio
quelle
es würde immer noch blocken, weil innodb mit auto_increment umgeht. Standardmäßig verwendet innodb beim Einfügen in eine auto_increment-Spalte eine Sperre auf Tabellenebene und gibt die Sperre frei, sobald das Einfügen abgeschlossen ist.
ovais.tariq