Erstellen Sie einen Index für eine riesige MySQL-Produktionstabelle ohne Tabellensperre

103

Ich muss einen Index für eine MySQL-Tabelle mit ~ 5 Millionen Zeilen erstellen. Es ist eine Produktionstabelle, und ich fürchte einen vollständigen Block von allem, wenn ich eine CREATE INDEX-Anweisung ausführe ...

Gibt es eine Möglichkeit, diesen Index zu erstellen, ohne Einfügungen und Auswahlen zu blockieren?

Ich frage mich nur, dass ich nicht anhalten, einen Index erstellen und mein System neu starten muss!

n0cturnal
quelle
1
Stellen Sie sicher, dass Ihre myisam_sort_buffer_size und myisam_max_sort_file_size groß genug sind.
Jon Black

Antworten:

129

[2017] Update: MySQL 5.6 unterstützt Online-Index-Updates

https://dev.mysql.com/doc/refman/8.0/en/innodb-online-ddl-operations.html#online-ddl-index-syntax-notes

In MySQL 5.6 und höher bleibt die Tabelle für Lese- und Schreibvorgänge verfügbar, während der Index erstellt oder gelöscht wird. Die Anweisung CREATE INDEX oder DROP INDEX wird erst beendet, nachdem alle Transaktionen, die auf die Tabelle zugreifen, abgeschlossen sind, sodass der Anfangszustand des Index den neuesten Inhalt der Tabelle widerspiegelt. Zuvor führte das Ändern der Tabelle während der Erstellung oder Löschung eines Index in der Regel zu einem Deadlock, durch den die Anweisung INSERT, UPDATE oder DELETE für die Tabelle abgebrochen wurde.

[2015] Das Aktualisieren der Tabelle zeigt Blockschreibvorgänge in MySQL 5.5 an

Aus der obigen Antwort:

"Wenn Sie eine Version verwenden, die größer als 5.1 ist, werden Indizes erstellt, während die Datenbank online ist. Machen Sie sich also keine Sorgen, dass Sie die Verwendung des Produktionssystems nicht unterbrechen."

Dies ist **** FALSE **** (zumindest für MyISAM / InnoDB-Tabellen, was 99,999% der Leute da draußen verwenden. Clustered Edition ist anders.)

Wenn Sie UPDATE-Operationen für eine Tabelle ausführen, wird BLOCKIERT, während der Index erstellt wird. MySQL ist wirklich sehr, sehr dumm (und ein paar andere Dinge).

Testskript:

(   
  for n in {1..50}; do
    #(time mysql -uroot -e 'select  * from website_development.users where id = 41225\G'>/dev/null) 2>&1 | grep real;
    (time mysql -uroot -e 'update website_development.users set bio="" where id = 41225\G'>/dev/null) 2>&1 | grep real;
  done
) | cat -n &
PID=$!
sleep 0.05
echo "Index Update - START"
mysql -uroot website_development -e 'alter table users add index ddopsonfu (last_name, email, first_name, confirmation_token, current_sign_in_ip);'
echo "Index Update - FINISH"
sleep 0.05
kill $PID
time mysql -uroot website_development -e 'drop index ddopsonfu on users;'

Mein Server (InnoDB):

Server version: 5.5.25a Source distribution

Ausgabe (beachten Sie, wie die 6. Operation für die ~ 400 ms blockiert, die zum Abschluss der Indexaktualisierung erforderlich sind):

 1  real    0m0.009s
 2  real    0m0.009s
 3  real    0m0.009s
 4  real    0m0.012s
 5  real    0m0.009s
Index Update - START
Index Update - FINISH
 6  real    0m0.388s
 7  real    0m0.009s
 8  real    0m0.009s
 9  real    0m0.009s
10  real    0m0.009s
11  real    0m0.009s

Vs Leseoperationen, die nicht blockieren (tauschen Sie den Zeilenkommentar im Skript aus):

 1  real    0m0.010s
 2  real    0m0.009s
 3  real    0m0.009s
 4  real    0m0.010s
 5  real    0m0.009s
Index Update - START
 6  real    0m0.010s
 7  real    0m0.010s
 8  real    0m0.011s
 9  real    0m0.010s
...
41  real    0m0.009s
42  real    0m0.010s
43  real    0m0.009s
Index Update - FINISH
44  real    0m0.012s
45  real    0m0.009s
46  real    0m0.009s
47  real    0m0.010s
48  real    0m0.009s

Aktualisieren des MySQL-Schemas ohne Ausfallzeiten

Bisher gibt es nur eine mir bekannte Methode, um ein MySQL-Schema zu aktualisieren und keinen Verfügbarkeitsausfall zu erleiden. Rundschreiben Meister:

  • Auf Master A läuft Ihre MySQL-Datenbank
  • Bringen Sie Master B in Betrieb und lassen Sie die Schreibvorgänge von Master A replizieren (B ist ein Slave von A).
  • Führen Sie das Schema-Update auf Master B durch. Es wird während des Upgrades ins Hintertreffen geraten
  • Lassen Sie Meister B aufholen. Invariant: Ihre Schemaänderung MUSS in der Lage sein, Befehle zu verarbeiten, die aus einem Downversion-Schema repliziert wurden. Änderungen an der Indizierung sind zulässig. Einfache Spaltenzusätze qualifizieren sich normalerweise. Spalte entfernen? wahrscheinlich nicht.
  • Tauschen Sie alle Clients ATOMISCH von Master A nach Master B. Wenn Sie sicher sein möchten (vertrauen Sie mir, das tun Sie), sollten Sie sicherstellen, dass der letzte Schreibvorgang in A VOR B nach B repliziert wirdB nimmt seinen ersten Schreibvorgang. Wenn Sie gleichzeitiges Schreiben an 2+ Master zulassen, ... verstehen Sie die MySQL-Replikation auf DEEP-Ebene besser oder Sie sind auf dem Weg in eine Welt voller Schmerzen. Extreme Schmerzen. Haben Sie eine Spalte, die AUTOINCREMENT ist? Sie sind geschraubt (es sei denn, Sie verwenden gerade Zahlen auf einem Master und Quoten auf dem anderen). Vertrauen Sie NICHT darauf, dass die MySQL-Replikation "das Richtige tut". Es ist NICHT klug und wird dich nicht retten. Es ist nur etwas weniger sicher, als binäre Transaktionsprotokolle von der Befehlszeile zu kopieren und von Hand wiederzugeben. Das Trennen aller Clients vom alten Master und das Umschalten auf den neuen Master kann jedoch in Sekundenschnelle erfolgen. Dies ist erheblich schneller als das Warten auf ein mehrstündiges Schema-Upgrade.
  • Jetzt ist Meister B dein neuer Meister. Sie haben das neue Schema. Das leben ist gut. Trink ein Bier; das Schlimmste ist vorbei.
  • Wiederholen Sie den Vorgang mit Master A und aktualisieren Sie sein Schema so, dass er Ihr neuer sekundärer Master wird, der für den Fall bereit ist, dass Ihr primärer Master (jetzt Master B) die Macht verliert oder einfach aufsteht und an Ihnen stirbt.

Eine einfache Möglichkeit, das Schema zu aktualisieren, ist dies nicht. In einer seriösen Produktionsumgebung funktionsfähig; Ja, so ist es. Bitte, bitte, bitte, wenn es eine einfachere Möglichkeit gibt, einer MySQL-Tabelle einen Index hinzuzufügen, ohne Schreibvorgänge zu blockieren, lassen Sie es mich wissen.

Googeln führte mich zu diesem Artikel, der eine ähnliche Technik beschreibt. Noch besser ist, dass sie empfehlen, an derselben Stelle im Verfahren zu trinken (Beachten Sie, dass ich meine Antwort geschrieben habe, bevor ich den Artikel gelesen habe)!

Perconas pt-online-Schema-Änderung

Der Artikel, den ich oben verlinkt habe, handelt von einem Tool, pt-online-schema-change , das wie folgt funktioniert:

  • Erstellen Sie eine neue Tabelle mit derselben Struktur wie das Original.
  • Aktualisieren Sie das Schema für eine neue Tabelle.
  • Fügen Sie der Originaltabelle einen Trigger hinzu, damit die Änderungen mit der Kopie synchron bleiben
  • Kopieren Sie Zeilen in Stapeln aus der Originaltabelle.
  • Schieben Sie den Originaltisch aus dem Weg und ersetzen Sie ihn durch einen neuen Tisch.
  • Lass den alten Tisch fallen.

Ich habe das Tool noch nie selbst ausprobiert. YMMV

RDS

Ich verwende derzeit MySQL über Amazon RDS . Es ist ein wirklich raffinierter Dienst, der MySQL einschließt und verwaltet, sodass Sie mit einer einzigen Schaltfläche neue Lesereplikate hinzufügen und die Datenbank über Hardware-SKUs hinweg transparent aktualisieren können. Es ist wirklich praktisch. Sie erhalten keinen SUPER-Zugriff auf die Datenbank, sodass Sie nicht direkt mit der Replikation schrauben können (ist dies ein Segen oder ein Fluch?). Sie können jedoch die Read Replica Promotion verwenden , um Ihre Schemaänderungen an einem schreibgeschützten Slave vorzunehmen, und diesen Slave dann zu Ihrem neuen Master heraufstufen. Genau der gleiche Trick wie oben beschrieben, nur viel einfacher auszuführen. Sie tun immer noch nicht viel, um Ihnen bei der Umstellung zu helfen. Sie müssen Ihre App neu konfigurieren und neu starten.

Dave Dopson
quelle
3
pt-online-schema-change funktioniert auch bei einer Master-Slave-Replikation hervorragend. Ich habe es verwendet, um eine Live-Migration in einer ausgelasteten Tabelle mit mehr als 20 Millionen Datensätzen auf unserer Produktionsmaster-Datenbank mit 2 Replikations-Slaves ohne Probleme oder Ausfallzeiten durchzuführen. Die Vorbereitung des Skripts dauert einige Zeit, und normalerweise muss ich eine SQL-Datei erstellen, die die unformatierte SQL-Änderung enthält, und eine SH-Datei als Wrapper, um dasselbe SQL auszuführen, jedoch im Fragmentformat (keine ALTER TABLE). Sie können mehrere Befehle mit pt-online-schema-change ausführen, indem Sie sie aneinanderreihen und durch Komma trennen.
Alex Le
-1; Ich weiß nichts über ältere Versionen, aber ich weiß, dass die Indexerstellung nicht die gleichzeitige DML in MySQL 5.6+ blockiert (für die zum Zeitpunkt der Erstellung dieser Antwort ein RC vorhanden war und die offiziell veröffentlicht wurde, als diese Antwort gültig war bearbeitet im Mai 2013), weil ich mich darauf verlassen habe, mehrstündige Indexerstellungen auf Produktionstabellen auszuführen, während ich immer noch Einfügungen akzeptiere. Und während Sie können Recht haben über Index DML in 5.5 Schaffung Sperrung und unten, die Unter zweiter Verzögerung hier gezeigt wird , nicht völlig überzeugend.
Mark Amery
@MarkAmery - Blockierungsverhalten blockiert Verhalten und 400 ms sind eine Ewigkeit. MySQL 5.5-Blöcke für Indexaktualisierungen. Erstellen Sie eine größere Testdatenbank, die für Sekunden, Stunden oder Tage blockiert wird. Ich habe diesen Beitrag geschrieben, bevor MySQL 5.6 Online-Schema-Updates hatte, daher spiegelt mein ursprünglicher Inhalt diese Tatsache nicht wider. Ich habe den Beitrag aktualisiert, um die neu verfügbaren Informationen wiederzugeben.
Dave Dopson
@ DaveDopson, sind Sie zu 100% sicher, dass nur UPDATE-Vorgänge blockiert sind?
toto_tico
Das war bei der von mir getesteten Version der Fall.
Dave Dopson
67

Wie in diesem Blogbeitrag beschrieben , wurde der InnoDB- ALTER TABLEMechanismus für MySQL 5.6 komplett neu gestaltet.

(Für einen exklusiven Überblick über dieses Thema bietet die MySQL-Dokumentation einen lesenswerten Nachmittag.)

Um einer Tabelle einen Index hinzuzufügen, ohne dass eine Sperre für UPDATE/ resultiert INSERT, kann das folgende Anweisungsformat verwendet werden:

ALTER TABLE my_table ADD INDEX my_table__idx (my_column), ALGORITHM=INPLACE, LOCK=NONE;
Drew
quelle
4
Vorsichtsmaßnahme
Alexander Torstling
16

MySQL 5.6-Update (Februar 2013): Sie können jetzt Lese- und Schreibvorgänge ausführen, während ein Index erstellt wird, auch mit InnoDB-Tabellen - http://dev.mysql.com/doc/refman/5.6/en/innodb-create-index -overview.html

In MySQL 5.6 und höher bleibt die Tabelle für Lese- und Schreibvorgänge verfügbar, während der Index erstellt oder gelöscht wird. Die Anweisung CREATE INDEX oder DROP INDEX wird erst beendet, nachdem alle Transaktionen, die auf die Tabelle zugreifen, abgeschlossen sind, sodass der Anfangszustand des Index den neuesten Inhalt der Tabelle widerspiegelt. Zuvor führte das Ändern der Tabelle während der Erstellung oder Löschung eines Index in der Regel zu einem Deadlock, durch den die Anweisung INSERT, UPDATE oder DELETE für die Tabelle abgebrochen wurde.

und:

In MySQL 5.6 wird diese Funktion allgemeiner: Sie können Tabellen lesen und in Tabellen schreiben, während ein Index erstellt wird, und viele weitere Arten von ALTER TABLE-Operationen können ausgeführt werden, ohne die Tabelle zu kopieren, ohne DML-Operationen zu blockieren oder beides. Daher bezeichnen wir in MySQL 5.6 und höher diese Funktionen normalerweise als Online-DDL und nicht als schnelle Indexerstellung.

von http://dev.mysql.com/doc/refman/5.6/en/glossary.html#glos_fast_index_creation

Eric Saboia
quelle
Wie kann dann Daves Analyse erklärt werden?
Nikhil Sahu
1
@NikhilSahu Dave hat offensichtlich nicht auf MySQL 5.6 getestet, sondern auf einer älteren Version. Beachten Sie, dass 5.6 noch nicht veröffentlicht wurde, als Dave die erste Überarbeitung seiner Antwort veröffentlichte.
Mark Amery
+1. Meine Analyse war auf MySQL 5.5 (das neueste, das 2013 verfügbar war). Ich aktualisiere meine Antwort, um die neuen Funktionen in MySQL 5.6 widerzuspiegeln.
Dave Dopson
3

pt-online-schema-change ist der richtige Weg, wenn Sie wirklich sicherstellen möchten, dass die Migration die Site nicht zum Erliegen bringt.

Wie ich im obigen Kommentar geschrieben habe, habe ich mehrere Erfahrungen mit pt-online-schema-change in der Produktion. Wir haben unsere Haupttabelle mit mehr als 20 Millionen Datensätzen und einen Master -> 2 schreibgeschützte Replikations-Slaves. Ich habe mindestens Dutzende von Migrationen mit pt-online-schema-change durchgeführt, vom Hinzufügen einer neuen Spalte, Ändern des Zeichensatzes bis zum Hinzufügen mehrerer Indizes. Wir bedienen auch während der Migrationszeit Tonnen von Verkehr und hatten keinen Schluckauf. Natürlich müssten Sie alle Skripte sehr gründlich testen, bevor Sie mit der Produktion beginnen.

Ich habe versucht, die Änderungen in einem Skript zusammenzufassen, sodass pt-online-schema-change die Daten nur einmal kopieren muss. Und seien Sie sehr vorsichtig beim Ändern des Spaltennamens, da Sie Ihre Daten verlieren. Das Hinzufügen eines Index sollte jedoch in Ordnung sein.

Alex Le
quelle
Ich bin mit Ihrer uneingeschränkten Empfehlung von nicht einverstanden pt-online-schema-change. Es ist großartig, aber in vielen Situationen, in denen die Online-DDL-Funktionen von MySQL 5.6 + bereits einwandfrei funktionieren, übertrieben. Es hat auch Einschränkungen (z. B. nicht gut mit Triggern zu spielen) und verdoppelt den Schreibaufwand pro Einfügung in die ursprüngliche Tabelle, während eine Schemaänderung ausgeführt wird. Es belastet Ihre Festplatte erheblich mehr als eine gewöhnliche Änderung des Online-Schemas und hat daher das Potenzial, Ihre Site unter Umständen "herunterzufahren", unter denen das einfache Ausführen der Schemaänderung auf einfache Weise gut funktioniert hätte.
Mark Amery
Ich habe aufgrund meiner tatsächlichen Erfahrung mit pt-online-schema-change zu der Zeit geschrieben, daher bin ich mir nicht sicher, warum Sie meine Empfehlung als "unqualifiziert" bezeichnen würden. Wir hatten zu jedem Zeitpunkt mindestens 1000 Besucher auf der Site, als ich die Schemaänderungen durchführte, und natürlich war die Festplatten-E / A-Belastung, aber unsere Site ging nicht aus. Gutes Caching hat auch geholfen. Ich habe MySQL 5.6+ Online-DDL nicht verwendet, aber meiner Erfahrung nach hat pt-online-schema-change in unserem Fall seine Arbeit gut gemacht.
Alex Le
1
@AlexYe Yikes, ich meinte "unqualifiziert" im Sinne von "ohne Vorbehalt" und nicht im Sinne von "von jemandem geliefert, der nicht zum Kommentieren qualifiziert ist" - die letztere Interpretation kam mir erst in den Sinn, als ich Ihren Kommentar sah und sicherlich nicht ist nicht das, was ich beabsichtigt habe! Das heißt, ich habe gesagt, dass es zwar pt-online-schema-changeein nützliches Tool ist, aber es gibt sehr viele Situationen, in denen gewöhnliches Online-DDL genauso gut ist und eine Handvoll, in denen es besser ist. Daher sollte jede Empfehlung sorgfältig und nicht universell abgewehrt werden.
Mark Amery