ALTER TABLE su una tabella di grandi dimensioni con una colonna indicizzata


14

Ho una grande tabella con una colonna VARCHAR (20) e devo modificarla per diventare una colonna VARCHAR (50). In genere, l'esecuzione di ALTER TABLE (aggiunta di TINYINT) su questa particolare tabella richiede circa 90-120 minuti per il completamento, quindi posso davvero farlo solo il sabato o la domenica sera per evitare di influenzare gli utenti del database. Se possibile, vorrei fare questa modifica prima di allora.

Anche la colonna è indicizzata, che presumo rallenterà ALTER TABLE, perché deve ricostruire l'indice dopo aver modificato la lunghezza della colonna.

L'app Web è configurata in un ambiente di replica MySQL (26 slave e un master). Ricordo una volta che ho letto da qualche parte che un metodo è prima di eseguire ALTER TABLE su ogni slave (riducendo al minimo l'impatto sugli utenti), quindi farlo sul Master, ma non tenterà quindi di replicare il comando ALTER TABLE sugli slave?

Quindi la mia domanda è: qual è il modo migliore per me di modificare questa tabella con un'interruzione minima per i miei utenti?

Modifica: la tabella è InnoDB.


aggiungendo quella colonna tinyint significava effettivamente aggiungere una colonna con un valore predefinito? Perché farlo su un tavolo enorme potrebbe richiedere molto tempo ..
Marian,

Risposte:


13

Se sei un po 'avventuroso, potresti prendere in mano la situazione eseguendo ALTER TABLE in fasi che puoi vedere. Supponiamo che la tabella che desideri modificare sia denominata WorkingTable. È possibile eseguire le modifiche in fasi come questa:

#
#  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;

Puoi eseguirlo su tutti gli schiavi. E il maestro ??? Come si impedisce a questo di replicarsi agli schiavi. Semplice: non inviare l'SQL nei registri binari del master. Basta chiudere la registrazione binaria nella sessione prima di fare le cose ALTER TABLE:

#
#  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;

Ma aspetta !!! Che dire di eventuali nuovi dati che arrivano durante l'elaborazione di questi comandi ??? Rinominare la tabella all'inizio dell'operazione dovrebbe fare il trucco. Consentire di modificare un po 'questo codice per impedire l'immissione di nuovi dati al riguardo:

#
#  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;
  • Lo script 1 può essere eseguito su qualsiasi slave che non ha i log binari abilitati
  • Lo script 2 può essere eseguito su qualsiasi slave su cui siano abilitati i registri binari
  • Lo script 3 può essere eseguito su un master o in qualsiasi altro luogo

Provaci !!!


2
Un problema che vedo è se la tabella contiene un campo "auto_increment". Ho fatto un test di base e sono stato sorpreso di vedere che CREATE TABLE .. LIKE non copia il valore auto_increment nella nuova tabella
Derek Downey,

1
@DTest: buona cattura e ottimo commento !!!. Credo che puoi recuperare il valore auto_increment dalla colonna AUTO_INCREMENT di information_schema.tables. Se la tabella non ha un campo AUTO_INCREMENT, la colonna AUTO_INCREMENT in information_schema.tables sarà NULL. Altrimenti, conterrà il valore AUTO_INCREMENT necessario. Immagino che possa essere copiato per estrarre quel valore non NULL e fare ALTER TABLE WorkingSet AUTO_INCREMENT = <somenumber>; appena prima di rinominare la tabella temporanea in WorkingSet.
RolandoMySQLDBA l'

@RolandoMySQLDBA Un'altra cosa che si può fare è creare la WorkingTableNew copiando le istruzioni "show create table WorkingTable" e cambiare il valore del campo auto_increment promuovendolo di un numero che è sicuro per te, e anche cambiare la colonna in varchar (50 ), quindi rinominare entrambi in una singola istruzione "rinomina tabella WorkingTable in WorkingTableOld, rinomina tabella WorkingTableNew in WorkingTable" L'esecuzione di entrambi i nomi in un singolo comando garantisce che l'inserimento non abbia esito negativo (verificato che questo sia Prod per la tabella che ottiene migliaia di inserti / i) Quindi puoi fare il comando "inserisci in ... da"
Gautam Somani il

4

La mia ipotesi dalla documentazione sarebbe che il semplice aumento del vincolo di lunghezza su a varcharnon causerebbe lo stesso problema dell'aggiunta di una colonna:

Per alcune operazioni, è possibile una tabella ALTER sul posto che non richiede una tabella temporanea:

Ma questo sembra essere contraddetto nei commenti su questa domanda SO .

MODIFICARE

Almeno su 5.0, penso di poter confermare che aumentare la lunghezza richiede effettivamente una tabella temporanea (o qualche altra operazione altrettanto costosa):

banco di prova:

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;

risultato:

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)

La modifica della dimensione del campo varchar implica la verifica che non si stia superando la nuova dimensione. Per gli aumenti delle dimensioni, questo segno può essere ottimizzato.
BillThor,

3

Ho pensato, vorrei dire che dal ENGINE=INNODB

Se si hanno vincoli di chiave esterna, non è possibile modificare e rinominare senza i vincoli che puntano alla vecchia tabella (ora rinominata). In seguito dovrai modificare o eliminare i vincoli per la durata.


Shabang !!! È così vero In tal caso, si può essere bloccati solo facendo la modifica della tabella sulla tabella originale. Per lo meno, potrebbe essere necessario giocare con vincoli di disabilitazione, prima che abbia luogo ALTER TABLE. +1 per quell'ottima cattura !!!
RolandoMySQLDBA
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.