ALTER TABLE für eine große Tabelle mit einer indizierten Spalte


14

Ich habe eine große Tabelle mit einer VARCHAR (20) -Spalte und muss diese ändern, um eine VARCHAR (50) -Spalte zu werden. Normalerweise dauert das Ausführen einer ALTER TABLE (Hinzufügen eines TINYINT) für diese bestimmte Tabelle etwa 90 bis 120 Minuten. Das kann ich also nur am Samstag- oder Sonntagabend tun, um die Benutzer der Datenbank nicht zu beeinträchtigen. Wenn möglich, möchte ich diese Änderung vorher vornehmen.

Die Spalte ist auch indiziert, was die ALTER TABLE vermutlich verlangsamt, da der Index nach dem Ändern der Spaltenlänge neu erstellt werden muss.

Die Web-App wird in einer MySQL-Replikationsumgebung eingerichtet (26 Slaves und ein Master). Ich erinnere mich, dass ich einmal irgendwo gelesen habe, dass eine Methode darin besteht, zuerst die ALTER TABLE auf jedem Slave auszuführen (um die Auswirkungen auf die Benutzer zu minimieren) und dies dann auf dem Master zu tun, aber versucht das dann nicht, den ALTER TABLE-Befehl auf die Slaves zu replizieren?

Meine Frage lautet also: Wie kann ich diese Tabelle am besten ändern, ohne meine Benutzer zu stören?

Edit: Die Tabelle ist InnoDB.


Das Hinzufügen dieser tinyint-Spalte bedeutete, tatsächlich eine Spalte mit einem Standardwert hinzuzufügen. Da dies auf einem riesigen Tisch sehr lange dauern kann ..
Marian

Antworten:


13

Wenn Sie ein wenig abenteuerlustig sind, können Sie die Dinge selbst in die Hand nehmen, indem Sie die ALTER-TABELLE in sichtbaren Schritten ausführen. Angenommen, die zu ändernde Tabelle heißt WorkingTable. Sie können die Änderungen in folgenden Schritten durchführen:

#
#  Script 1
#  Alter table structure of a single column of a large table
#
CREATE TABLE WorkingTableNew LIKE WorkingTable;
ALTER TABLE WorkingTableNew MODIFY BigColumn VARCHAR(50);
INSERT INTO WorkingTableNew SELECT * FROM WorkingTable;
ALTER TABLE WorkingTable RENAME WorkingTableOld;
ALTER TABLE WorkingTableNew RENAME WorkingTable;
DROP TABLE WorkingTableOld;

Dies können Sie an allen Slaves durchführen. Was ist mit dem Meister ??? Wie verhindern Sie, dass dies auf die Slaves repliziert wird? Einfach: Senden Sie die SQL nicht in die Binärprotokolle des Masters. Beenden Sie einfach die Binärprotokollierung in der Sitzung, bevor Sie die ALTER TABLE-Aufgaben ausführen:

#
#  Script 2
#  Alter table structure of a single column of a large table
#  while preventing it from replicating to slaves
#
SET SQL_LOG_BIN = 0;
CREATE TABLE WorkingTableNew LIKE WorkingTable;
ALTER TABLE WorkingTableNew MODIFY BigColumn VARCHAR(50);
INSERT INTO WorkingTableNew SELECT SQL_NO_CACHE * FROM WorkingTable;
ALTER TABLE WorkingTable RENAME WorkingTableOld;
ALTER TABLE WorkingTableNew RENAME WorkingTable;
DROP TABLE WorkingTableOld;

Aber warte !!! Was ist mit neuen Daten, die während der Verarbeitung dieser Befehle eingehen? Das Umbenennen der Tabelle zu Beginn der Operation sollte ausreichen. Ändern Sie diesen Code ein wenig, um zu verhindern, dass diesbezüglich neue Daten eingegeben werden:

#
#  Script 3
#  Alter table structure of a single column of a large table
#  while preventing it from replicating to slaves
#  and preventing new data from entering into the old table
#
SET SQL_LOG_BIN = 0;
ALTER TABLE WorkingTable RENAME WorkingTableOld;
CREATE TABLE WorkingTableNew LIKE WorkingTableOld;
ALTER TABLE WorkingTableNew MODIFY BigColumn VARCHAR(50);
INSERT INTO WorkingTableNew SELECT SQL_NO_CACHE * FROM WorkingTableOld;
ALTER TABLE WorkingTableNew RENAME WorkingTable;
DROP TABLE WorkingTableOld;
  • Skript 1 kann auf jedem Slave ausgeführt werden, für den keine Binärprotokolle aktiviert sind
  • Skript 2 kann auf jedem Slave ausgeführt werden, für den Binärprotokolle aktiviert sind
  • Skript 3 kann auf einem Master oder an einem anderen Ort ausgeführt werden

Versuche es !!!


2
Ein Problem, das ich sehe, ist, wenn die Tabelle ein 'auto_increment'-Feld enthält. Ich habe einen Basistest durchgeführt und war überrascht, dass CREATE TABLE .. LIKE den auto_increment-Wert nicht in die neue Tabelle kopiert
Derek Downey,

1
@ DTest: Guter Fang und toller Kommentar !!!. Ich glaube, Sie können den auto_increment-Wert aus der information_schema.tables-Spalte AUTO_INCREMENT abrufen. Wenn die Tabelle kein AUTO_INCREMENT-Feld enthält, ist die AUTO_INCREMENT-Spalte in information_schema.tables NULL. Andernfalls enthält es den benötigten AUTO_INCREMENT-Wert. Ich denke, es kann ein Skript erstellt werden, um diesen Nicht-NULL-Wert zu extrahieren und ALTER TABLE WorkingSet AUTO_INCREMENT = <somenumber>; kurz vor dem Umbenennen der temporären Tabelle zurück zu WorkingSet.
RolandoMySQLDBA

@RolandoMySQLDBA Sie können auch WorkingTableNew erstellen, indem Sie die Anweisungen "show create table WorkingTable" kopieren und den Wert des auto_increment-Felds ändern, indem Sie es durch eine für Sie sichere Zahl erweitern, und die Spalte in varchar (50) ändern ) und dann beide in einer Anweisung umbenennen "Tabelle umbenennen WorkingTable zu WorkingTableOld, Tabelle umbenennen WorkingTableNew zu WorkingTable" Wenn beide Umbenennungen in einem einzigen Befehl ausgeführt werden, wird sichergestellt, dass keine Einfügung fehlschlägt Sie können den Befehl "Einfügen in ... von" ausführen
Gautam Somani

4

Aus der Dokumentation gehe ich davon aus, dass das bloße Erhöhen der Längenbeschränkung für a varcharnicht den gleichen Aufwand verursacht wie das Hinzufügen einer Spalte:

Für einige Operationen ist eine vorhandene ALTER TABLE möglich, für die keine temporäre Tabelle erforderlich ist:

Dies scheint jedoch in den Kommentaren zu dieser SO- Frage zu widersprechen .

BEARBEITEN

Zumindest in Version 5.0 kann ich bestätigen, dass für das Erhöhen der Länge tatsächlich eine temporäre Tabelle (oder eine andere, ebenfalls teure Operation) erforderlich ist:

Prüfstand:

create table my_table (id int auto_increment primary key, varchar_val varchar(10));
insert into my_table (varchar_val)
select 'HELLO'
from (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) s1,
     (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) s2,
     (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) s3,
     (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) s4,
     (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) s5,
     (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) s6;

Ergebnis:

alter table my_table modify varchar_val varchar(20);
Query OK, 1000000 rows affected (2.91 sec)

alter table my_table add int_val int;
Query OK, 1000000 rows affected (2.86 sec)

Wenn Sie die Größe des varchar-Felds ändern, müssen Sie sicherstellen, dass Sie die neue Größe nicht überschreiten. Bei Vergrößerungen kann dieses Häkchen wegoptimiert werden.
BillThor

3

Ich dachte, ich würde das seit dem erwähnen ENGINE=INNODB

Wenn Sie Fremdschlüsseleinschränkungen haben, können Sie diese nicht ändern und umbenennen, ohne dass Ihre Einschränkungen auf die alte Tabelle (jetzt umbenannt) verweisen. Sie müssen danach die Bedingungen ändern oder für die Dauer löschen.


Shabang !!! Das ist so wahr. In diesem Fall kann man nur mit alter table auf dem Originaltisch feststecken. Möglicherweise müssen Sie zumindest Spiele mit deaktivierenden Einschränkungen spielen, bevor ALTER TABLE ausgeführt wird. +1 für diesen sehr guten Fang !!!
RolandoMySQLDBA
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.