ALTER TABLE ADD COLUMN dauert lange

73

Ich habe nur versucht, einer Tabelle (main_table) in einer Datenbank eine Spalte mit dem Namen "location" hinzuzufügen. Der Befehl, den ich ausführe, war

ALTER TABLE main_table ADD COLUMN location varchar (256);

Die main_table enthält> 2.000.000 Zeilen. Es läuft länger als 2 Stunden und ist immer noch nicht fertig.

Ich habe versucht, mytop die Aktivität dieser Datenbank zu überwachen, um sicherzustellen, dass die Abfrage nicht durch einen anderen Abfrageprozess gesperrt ist, aber es scheint nicht. Soll es so lange dauern? Eigentlich habe ich den Computer gerade neu gestartet, bevor ich diesen Befehl ausgeführt habe. Jetzt läuft dieser Befehl noch. Ich bin mir nicht sicher, was ich tun soll.

Fanchyna
quelle
Aufgrund der Indizes und der Anzahl der in der Tabelle vorhandenen Zeilen wird dies lange dauern. Anmerkung: Varchar (255)
Jauzsika
Ich denke, Sie sollten einen Standardwert dafür angegeben haben. Vielleicht braucht es deshalb Zeit?
Nilesh
2
NULLIn diesem Fall wird standardmäßig nicht angegeben, warum es lange dauert.
Romain
MySQL 8.0.12 hat einenALTER TABLE .. ADD COLUMN .. ALGORITHM=INSTANT
Rick James

Antworten:

191

Ihre ALTER TABLEAussage impliziert, dass MySQL jede einzelne Zeile der Tabelle einschließlich der neuen Spalte neu schreiben muss. Da Sie mehr als 2 Millionen Zeilen haben, würde ich definitiv erwarten, dass es eine beträchtliche Zeit in Anspruch nimmt, während der Ihr Server wahrscheinlich größtenteils E / A-gebunden sein wird. Normalerweise ist es performanter, Folgendes zu tun:

CREATE TABLE main_table_new LIKE main_table;
ALTER TABLE main_table_new ADD COLUMN location VARCHAR(256);
INSERT INTO main_table_new SELECT *, NULL FROM main_table;
RENAME TABLE main_table TO main_table_old, main_table_new TO main_table;
DROP TABLE main_table_old;

Auf diese Weise fügen Sie die Spalte in die leere Tabelle ein und schreiben im Grunde genommen die Daten in diese neue Tabelle, von denen Sie sicher sind, dass sie niemand anderes betrachtet, ohne so viele Ressourcen zu sperren.

Romain
quelle
1
@ Malvolio erwähnenswert: Ich denke, es ist eine MySQL-proprietäre SQL-Erweiterung ... Ich bin mir jedoch nicht 100% sicher.
Romain
6
Selbst wenn ja, wann hat das letzte Mal jemand ein Nicht-MySQL-SQL-RDBMS verwendet? Freunde lassen Freunde Oracle nicht kaufen.
Malvolio
6
Das dynamische Ausfüllen von "fields_in_main_table" wäre jetzt eine großartige Sache für die Verwendung in einem Skript.
Marki
5
Auch diese Methode muss jedoch die gesamte Tabelle neu schreiben. Habe ich Recht, wenn ich denke, dass der einzige Vorteil dieses Ansatzes darin besteht, dass die aktuelle Tabelle beim Hinzufügen der neuen Spalte verwendet werden kann?
Michael
5
Dies hilft Ihnen nicht, wenn Sie Fremdschlüssel haben, die auf die Tabelle verweisen.
Jonny
31

Ich denke, die passende Antwort hierfür ist die Verwendung einer Funktion wie pt-online-schema-change oder gh-ost .

Wir haben damit eine Migration von über 4 Milliarden Zeilen durchgeführt, obwohl dies bis zu 10 Tage dauern kann, mit weniger als einer Minute Ausfallzeit.

Percona arbeitet sehr ähnlich wie oben

  • Erstellen Sie eine temporäre Tabelle
  • Erstellt Trigger für die erste Tabelle (für Einfügungen, Aktualisierungen, Löschungen), damit sie in die temporäre Tabelle repliziert werden
  • Migrieren Sie Daten in kleinen Stapeln
  • Wenn Sie fertig sind, benennen Sie die Tabelle in eine neue Tabelle um und löschen Sie die andere Tabelle
Pratik Bothra
quelle
3
Dies ist die Art von Frage, die beweist, dass weniger gewählte oder akzeptierte Lösungen manchmal die besten oder nur richtigen Lösungen sind. Wie viele Leute stimmen für die andere Lösung, die falsch ist OMG.
Raul R.
@ RaulR. Dies hängt vom Anwendungsfall ab. Die andere Lösung war in meinem Anwendungsfall reichlich "richtig", nicht jeder arbeitet rund um die Uhr an einer Produktionsumgebung.
Austin Schmidt
1

Das Ändern von Tabellen dauert bei großen Datenmengen wie in Ihrem Fall sehr lange. Vermeiden Sie es daher, sie in solchen Situationen zu verwenden, und verwenden Sie Code wie diesen:

select main_table.*, 
  cast(null as varchar(256)) as null_location, -- any column you want accepts null
  cast('' as varchar(256)) as not_null_location, --any column doesn't accept null
  cast(0 as int) as not_null_int, -- int column doesn't accept null
into new_table 
from main_table;

drop table main_table;
rename table new_table TO main_table;
ZORRO_BLANCO
quelle