ALTER TABLE en una tabla grande con una columna indexada

14

Tengo una tabla grande con una columna VARCHAR (20), y necesito modificarla para convertirme en una columna VARCHAR (50). Por lo general, realizar una TABLA DE ALTERACIÓN (agregar un TINYINT) en esta tabla en particular toma aproximadamente 90-120 minutos en completarse, por lo que realmente solo puedo hacerlo un sábado o domingo por la noche para evitar afectar a los usuarios de la base de datos. Si es posible, me gustaría hacer esta modificación antes de eso.

La columna también está indexada, lo que supongo hará que ALTER TABLE sea más lenta, ya que tiene que reconstruir el índice después de modificar la longitud de la columna.

La aplicación web está configurada en un entorno de replicación MySQL (26 esclavos y un maestro). Recuerdo que una vez que leí en alguna parte que un método es primero realizar ALTER TABLE en cada esclavo (minimizando el impacto en los usuarios), luego hacer esto en el Master, pero ¿eso no intentará replicar el comando ALTER TABLE a los esclavos?

Entonces mi pregunta es: ¿cuál es la mejor manera de modificar esta tabla con una interrupción mínima para mis usuarios?

Editar: la tabla es InnoDB.

Matt Healy
fuente
agregar esa columna tinyint significaba realmente agregar una columna con un valor predeterminado? Porque hacerlo en una mesa enorme podría llevar mucho tiempo ...
Marian

Respuestas:

13

Si eres un poco aventurero, puedes tomar el asunto en tus manos realizando ALTER TABLE en etapas que puedas ver. Supongamos que la tabla que desea cambiar se llama WorkingTable. Puede realizar los cambios en etapas como esta:

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

Puedes realizar esto en todos los esclavos. ¿Qué hay del maestro? ¿Cómo evitas que esto se replique a los esclavos? Simple: no envíe el SQL a los registros binarios del maestro. Simplemente apague el registro binario en la sesión antes de hacer las cosas 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;

Pero espera !!! ¿Qué pasa con los datos nuevos que ingresan al procesar estos comandos? Cambiar el nombre de la tabla al comienzo de la operación debería ser el truco. Dejemos alterar un poco este código para evitar ingresar datos nuevos a ese respecto:

#
#  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;
  • El script 1 se puede ejecutar en cualquier esclavo que no tenga registros binarios habilitados
  • El script 2 puede ejecutarse en cualquier esclavo que tenga registros binarios habilitados
  • Script 3 puede ejecutarse en un maestro o en cualquier otro lugar

Darle una oportunidad !!!

RolandoMySQLDBA
fuente
2
Un problema que veo es si la tabla contiene un campo 'auto_increment'. Hice una prueba básica y me sorprendió ver que CREATE TABLE .. LIKE no copia el valor de auto_increment en la nueva tabla
Derek Downey
1
@DTest: ¡Buena captura y excelente comentario! Creo que puede recuperar el valor de auto_increment de la columna information_schema.tables AUTO_INCREMENT. Si la tabla no tiene un campo AUTO_INCREMENT, entonces la columna AUTO_INCREMENT en information_schema.tables será NULL. De lo contrario, contendrá el valor AUTO_INCREMENT necesario. Supongo que se puede programar para extraer ese valor no NULL y hacer ALTER TABLE WorkingSet AUTO_INCREMENT = <somenumber>; justo antes de cambiar el nombre de la tabla temporal a WorkingSet.
RolandoMySQLDBA
@RolandoMySQLDBA Otra cosa que se puede hacer es crear WorkingTableNew copiando las instrucciones "show create table WorkingTable" y cambiar el valor del campo auto_increment avanzando por un número que sea seguro para usted, y también cambiar la columna a varchar (50 ), y luego renombrar ambos en una sola declaración "renombrar tabla WorkingTable a WorkingTableOld, renombrar tabla WorkingTableNew to WorkingTable" Ejecutar ambos renombramientos en un solo comando asegura que no fallarán las inserciones (probado esto es Prod para la tabla que obtiene 1000s de insertos / s) Luego puedes hacer el comando "insertar en ... desde"
Gautam Somani
4

Mi suposición de la documentación sería que el simple aumento de la restricción de longitud en un varcharno causaría el mismo problema que agregar una columna:

Para algunas operaciones, es posible una ALTER TABLE in situ que no requiere una tabla temporal:

Pero eso parece contradecirse en los comentarios sobre esta pregunta SO .

EDITAR

Al menos en 5.0, creo que puedo confirmar que aumentar la longitud de hecho requiere una tabla temporal (u otra operación igualmente costosa):

banco de pruebas:

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;

resultado:

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)
Jack dice que intente topanswers.xyz
fuente
Modificar el tamaño del campo varchar implica verificar que no esté excediendo el nuevo tamaño. Para aumentos de tamaño, esta verificación PUEDE optimizarse lejos.
BillThor
3

Pensé, lo mencionaría desde el ENGINE=INNODB

Si tiene restricciones de clave externa, no puede alterar y cambiar el nombre sin sus restricciones apuntando a la tabla anterior (ahora renombrada). Tendrás que modificar después o eliminar las restricciones por la duración.

randomx
fuente
Shabang !!! Eso es tan cierto. En ese caso, uno solo puede quedarse atascado haciendo la tabla alter en la tabla original. Por lo menos, es posible que tengas que jugar juegos con restricciones de desactivación, antes de que tenga lugar la ALTER TABLE. ¡+1 por esa muy buena captura!
RolandoMySQLDBA