web-dev-qa-db-fra.com

Alter table sur une grande table avec une colonne indexée

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

La colonne est également indexée, que je présume de rendre la table Alter plus lente, car elle doit reconstruire l'index après la modification de la longueur de la colonne.

L'application Web est configurée dans un environnement de réplication MySQL (26 esclaves et un maître). Je me souviens une fois en train de lire quelque part qu'une méthode consiste à effectuer d'abord le tableau alternatif sur chaque esclave (minimiser l'impact sur les utilisateurs), puis faites cela sur le maître, mais cela n'essaiera pas de reproduire la commande d'altération des esclaves?

Ma question est donc la suivante: quelle est la meilleure façon pour moi de modifier cette table avec une perturbation minimale de mes utilisateurs?

EDIT: La table est innodub.

14
Matt Healy

Si vous êtes un peu aventureux, vous pouvez prendre la matière entre vos mains en effectuant la table d'alimentation des étapes que vous pouvez voir. Supposons que la table que vous souhaitez modifier s'appelle Technicalttable. Vous pouvez effectuer les changements de stades comme celui-ci:

#
#  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. Qu'en est-il du maître ??? Comment empêchez-vous cela de reproduire aux esclaves. Simple: N'envoyez pas le SQL dans les journaux binaires du maître. Simplement fermez la journalisation binaire dans la session avant de faire la table d'alimentation:

#
#  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 entrent dans le traitement de ces commandes ??? Renommer la table au début de l'opération devrait faire le tour. Laissez altérer ce code un peu pour éviter d'entrer 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 qui ne dispose pas de journaux binaires activés
  • Le script 2 peut être exécuté sur n'importe quel esclave qui contient des journaux binaires activés
  • Le script 3 peut être exécuté sur un maître ou ailleurs

Essaie !!!

13
RolandoMySQLDBA

Je suppose que de la documentation serait que l'augmentation de la contrainte de longueur sur un varchar ne causerait pas de même problème que d'ajouter une colonne:

Pour certaines opérations, une table altérée sur place est possible qui ne nécessite pas de table temporaire:

Mais cela semble être contredit dans les commentaires à ce sujet DONC question.

ÉDITER

Au moins sur 5.0, je pense que je peux confirmer que l'augmentation de la longueur nécessite effectivement une table temporaire (ou une autre opération également coûteuse):

testbed:

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)

Je pensais que je mentionais que depuis le ENGINE=INNODB

Si vous avez des contraintes de clé étrangère, vous ne pouvez pas modifier et renommer avec vos contraintes indiquant l'ancienne table (maintenant renommée). Vous devrez modifier après ou laisser tomber les contraintes pour la durée.

3
randomx