ALTER TABLE sur une grande table avec une colonne indexée

14

J'ai une grande table avec une colonne VARCHAR (20), et je dois la modifier pour devenir une colonne VARCHAR (50). En règle générale, l'exécution d'une ALTER TABLE (ajout d'un TINYINT) sur cette table particulière prend environ 90 à 120 minutes, donc je ne peux vraiment le faire que le samedi ou le dimanche soir pour éviter d'affecter les utilisateurs de la base de données. Si possible, je voudrais faire cette modification avant cela.

La colonne est également indexée, ce qui, je présume, ralentira ALTER TABLE, car elle doit reconstruire l'index après avoir modifié la longueur de la colonne.

L'application Web est installée dans un environnement de réplication MySQL (26 esclaves et un maître). Je me souviens d'avoir lu quelque part qu'une méthode consiste à exécuter d'abord la ALTER TABLE sur chaque esclave (minimiser l'impact sur les utilisateurs), puis à le faire sur le maître, mais cela n'essaiera-t-il pas alors de répliquer la commande ALTER TABLE sur les esclaves?

Ma question est donc: quelle est la meilleure façon pour moi de modifier ce tableau avec un minimum de perturbations pour mes utilisateurs?

Edit: la table est InnoDB.

Matt Healy
la source
ajouter cette colonne tinyint signifiait réellement ajouter une colonne avec une valeur par défaut? Parce que faire ça sur une immense table peut prendre beaucoup de temps ..
Marian

Réponses:

13

Si vous êtes un peu aventureux, vous pouvez prendre les choses en main en effectuant la ALTER TABLE par étapes que vous pouvez voir. Supposons que la table que vous souhaitez modifier s'appelle WorkingTable. Vous pouvez effectuer les modifications par étapes comme ceci:

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

Vous pouvez effectuer cela sur tous les esclaves. Et le maître ??? Comment empêchez-vous cela de se répliquer sur les esclaves. Simple: N'envoyez pas le SQL dans les journaux binaires du maître. Fermez simplement la journalisation binaire dans la session avant de faire les choses 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;

Mais attendez !!! Qu'en est-il des nouvelles données qui arrivent lors du traitement de ces commandes ??? Renommer la table au début de l'opération devrait faire l'affaire. Modifions un peu ce code pour éviter de saisir de nouvelles données à cet égard:

#
#  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;
  • Le script 1 peut être exécuté sur n'importe quel esclave sur lequel les journaux binaires ne sont pas activés
  • Le script 2 peut être exécuté sur n'importe quel esclave dont les journaux binaires sont activés
  • Le script 3 peut être exécuté sur un maître ou n'importe où ailleurs

Essaie !!!

RolandoMySQLDBA
la source
2
Un problème que je vois est que la table contient un champ 'auto_increment'. J'ai fait un test de base et j'ai été surpris de voir que CREATE TABLE .. LIKE ne copie pas la valeur auto_increment dans la nouvelle table
Derek Downey
1
@DTest: Good Catch and Great Comment !!!. Je pense que vous pouvez récupérer la valeur auto_increment de la colonne information_schema.tables AUTO_INCREMENT. Si la table n'a pas de champ AUTO_INCREMENT, la colonne AUTO_INCREMENT dans information_schema.tables sera NULL. Sinon, il contiendra la valeur AUTO_INCREMENT nécessaire. Je suppose qu'il peut être scripté pour extraire cette valeur non NULL et faire ALTER TABLE WorkingSet AUTO_INCREMENT = <somenumber>; juste avant de renommer la table temporaire en WorkingSet.
RolandoMySQLDBA
@RolandoMySQLDBA Une autre chose que vous pouvez faire est de créer le WorkingTableNew en copiant les instructions "show create table WorkingTable" et de changer la valeur du champ auto_increment en le faisant avancer par un nombre qui est sûr pour vous, et aussi de changer la colonne en varchar (50 ), puis effectuez les deux renommer en une seule instruction "renommer la table WorkingTable en WorkingTableOld, renommer la table WorkingTableNew en WorkingTable" L'exécution des deux renommages en une seule commande garantit qu'aucune insertion n'échouera (testé, il s'agit de Prod pour la table qui obtient des milliers d'inserts / s), puis vous pouvez faire la commande "insérer dans ... à partir de"
Gautam Somani
4

Ma conjecture de la documentation serait que simplement augmenter la contrainte de longueur sur un varcharne causerait pas le même problème que l'ajout d'une colonne:

Pour certaines opérations, une ALTER TABLE en place est possible qui ne nécessite pas de table temporaire:

Mais cela semble être contredit dans les commentaires sur cette question SO .

ÉDITER

Au moins sur 5.0, je pense pouvoir confirmer que l'augmentation de la longueur nécessite en effet une table temporaire (ou une autre opération tout aussi chère):

banc d'essai:

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;

résultat:

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 dit d'essayer topanswers.xyz
la source
La modification de la taille du champ varchar implique de vérifier que vous ne dépassez pas la nouvelle taille. Pour les augmentations de taille, cette vérification PEUT être optimisée.
BillThor
3

Je pensais, je mentionnerais que depuis le ENGINE=INNODB

Si vous avez des contraintes de clé étrangère, vous ne pouvez pas modifier et renommer sans vos contraintes pointant vers l'ancienne table (maintenant renommée). Vous devrez ensuite modifier ou supprimer les contraintes pour la durée.

randomx
la source
Shabang !!! C'est tellement vrai. Dans ce cas, on ne peut être bloqué qu'en faisant alter table sur la table d'origine. À tout le moins, vous devrez peut-être jouer à des jeux avec des contraintes de désactivation avant que ALTER TABLE ait lieu. +1 pour cette très bonne prise !!!
RolandoMySQLDBA