ALTER TABLE sans verrouiller la table?

107

Lorsque vous exécutez une instruction ALTER TABLE dans MySQL, la table entière est verrouillée en lecture (autorisant les lectures simultanées, mais interdisant les écritures simultanées) pendant la durée de l'instruction. S'il s'agit d'une grande table, les instructions INSERT ou UPDATE peuvent être bloquées pendant un temps looooong. Existe-t-il un moyen de faire une "modification à chaud", comme l'ajout d'une colonne de telle sorte que la table puisse toujours être mise à jour tout au long du processus?

La plupart du temps, je suis intéressé par une solution pour MySQL, mais je serais intéressé par d'autres SGBDR si MySQL ne peut pas le faire.

Pour clarifier, mon objectif est simplement d'éviter les temps d'arrêt lorsqu'une nouvelle fonctionnalité nécessitant une colonne de table supplémentaire est mise en production. Tout schéma de base de données va changer au fil du temps, qui est juste un fait de la vie. Je ne vois pas pourquoi nous devrions accepter que ces changements entraînent inévitablement des temps d'arrêt; c'est juste faible.

Daniel
la source
2
Vous devez vous demander combien de fois vous allez modifier le tableau?
Allain Lalonde
1
À mon humble avis, les changements de schéma de base de données sont associés à de toutes nouvelles versions - ils ne sont pas déployés sporadiquement comme le font d'autres changements. C'est forcément un gros problème.
dkretz
9
@AllainLalonde - plus de 0 fois rend cette question légitime, surtout si un temps d'arrêt dans votre système coûterait des vies ou beaucoup d'argent. Et en tout cas, de nouvelles exigences logicielles apparaissent parfois.
Nathan Long

Réponses:

60

La seule autre option est de faire manuellement ce que de nombreux systèmes SGBDR font de toute façon ...
- Créer une nouvelle table

Vous pouvez ensuite copier le contenu de l'ancienne table sur un morceau à la fois. Tout en étant toujours prudent de tout INSERT / UPDATE / DELETE sur la table source. (Peut être géré par un déclencheur. Bien que cela entraînerait un ralentissement, ce n'est pas un verrou ...)

Une fois terminé, changez le nom de la table source, puis changez le nom de la nouvelle table. De préférence dans une transaction.

Une fois terminé, recompilez toutes les procédures stockées, etc. qui utilisent cette table. Les plans d'exécution ne seront probablement plus valides.

ÉDITER:

Certains commentaires ont été faits sur le fait que cette limitation est un peu faible. Alors j'ai pensé mettre une nouvelle perspective dessus pour montrer pourquoi c'est comme ça ...

  • Ajouter un nouveau champ revient à changer un champ sur chaque ligne.
  • Les verrous de champ seraient beaucoup plus difficiles que les verrous de ligne, sans parler des verrous de table.

  • Vous changez en fait la structure physique du disque, chaque enregistrement bouge.
  • C'est vraiment comme une MISE À JOUR sur la table entière, mais avec plus d'impact ...
MatBailie
la source
2
Et ayez un plan de test complet avant de changer. Si cela échoue, recommencez.
dkretz
2
Gérer la synchronisation via des déclencheurs était une bonne idée. J'utilise MySQL depuis si longtemps que j'oublie sans cesse qu'ils ont des déclencheurs maintenant. J'ai utilisé cette technique et maintenant j'ai un script hot-alter fonctionnel. Avec une barre de progression. Et cela fonctionne avec MyISAM. La vie est belle.
Daniel le
2
+1 C'est littéralement ce que fait SQL Enterprise Manager dans les coulisses lorsque vous apportez certains types de modifications de table dans l'interface utilisateur. Dans SQL 2008, ils ont en fait ajouté un avertissement afin que l'utilisateur sache qu'il exécute cette action drastique.
BradC
2
Vous n'avez rien mentionné à propos des clés étrangères référençant les tables en cours de modification. N'est-ce pas un problème?
Rafay
2
@MohammadRafayAleem - Champs et AUTOINCREMENT, vues, déclencheurs, etc. Mais même ainsi, l' approche est toujours réalisable.
MatBailie
42

Percona crée un outil appelé pt-online-schema-change qui permet de le faire.

Il fait essentiellement une copie de la table et modifie la nouvelle table. Pour garder la nouvelle table synchronisée avec l'original, il utilise des déclencheurs pour se mettre à jour. Cela permet d'accéder à la table d'origine pendant que la nouvelle table est préparée en arrière-plan.

Ceci est similaire à la méthode suggérée par Dems ci-dessus, mais cela se fait de manière automatisée.

Certains de leurs outils ont une courbe d'apprentissage, à savoir la connexion à la base de données, mais une fois que vous l'avez, ce sont d'excellents outils à avoir.

Ex:

pt-online-schema-change --alter "ADD COLUMN c1 INT" D=db,t=numbers_are_friends
SeanDowney
la source
Il semble que le lien soit rompu. J'ai trouvé que ce lien fonctionnait.
Noam Ben Ari
25

Cette question de 2009. Maintenant, MySQL propose une solution:

DDL en ligne (langage de définition de données)

Une fonctionnalité qui améliore les performances, la concurrence et la disponibilité des tables InnoDB pendant les opérations DDL (principalement ALTER TABLE). Voir Section 14.11, «InnoDB et DDL en ligne» pour plus de détails.

Les détails varient selon le type d'opération. Dans certains cas, la table peut être modifiée simultanément pendant que ALTER TABLE est en cours. L'opération peut être effectuée sans effectuer de copie de table ou en utilisant un type de copie de table spécialement optimisé. L'utilisation de l'espace est contrôlée par l'option de configuration innodb_online_alter_log_max_size.

Il vous permet d'ajuster l'équilibre entre les performances et la concurrence pendant l'opération DDL, en choisissant de bloquer entièrement l'accès à la table (clause LOCK = EXCLUSIVE), d'autoriser les requêtes mais pas de DML (clause LOCK = SHARED), ou d'autoriser les requêtes complètes et DML accès à la table (clause LOCK = NONE). Lorsque vous omettez la clause LOCK ou spécifiez LOCK = DEFAULT, MySQL autorise autant de concurrence que possible en fonction du type d'opération.

Si possible, effectuer des modifications sur place, plutôt que de créer une nouvelle copie de la table, évite les augmentations temporaires de l'utilisation de l'espace disque et de la surcharge d'E / S associées à la copie de la table et à la reconstruction des index secondaires.

voir MySQL 5.6 Reference Manual -> InnoDB and Online DDL pour plus d'informations.

Il semble que DDL en ligne soit également disponible dans MariaDB

Vous pouvez également utiliser ALTER ONLINE TABLE pour vous assurer que votre ALTER TABLE ne bloque pas les opérations simultanées (ne prend aucun verrou). C'est équivalent à LOCK = NONE.

MariaDB KB sur ALTER TABLE

Ivanov
la source
3
C'est dommage qu'il n'y ait pas d'autre moyen que les votes de faire flotter cela vers le haut, étant donné que cela annule principalement toutes les autres réponses simplement parce qu'elles ne font plus référence à la version actuelle de MySQL.
Burhan Ali
14

Je recommande Postgres si c'est une option. Avec postgres, il n'y a pratiquement pas de temps d'arrêt avec les procédures suivantes:

Une autre fonctionnalité intéressante est que la plupart des instructions DDL sont transactionnelles, vous pouvez donc effectuer une migration entière dans une transaction SQL, et si quelque chose ne va pas, tout est annulé.

Je l'ai écrit il y a un peu, cela peut peut-être nous éclairer davantage sur les autres mérites.

mikelikespie
la source
6
Postgres crée toujours un verrou exclusif sur l'alter, empêchant les autres de lire à partir de cette table.
clofresh
5
Je ne suis pas d'accord avec le bit «essentiellement pas de temps d'arrêt». Comme clofresh l'a dit, ALTER TABLE attrape un verrou exclusif sur la table bloquant toutes les lectures et écritures simultanées. D'après mon expérience, pour les tables actives la plupart du temps, vous n'obtiendrez même pas le verrou (ALTER TABLE mourra de faim). Et avec les transactions, vous pouvez facilement vous retrouver avec des blocages si vous n'êtes pas extrêmement prudent. Pour cette raison, je fixe toujours des temps d'arrêt lors de la modification de tables existantes dans Postgres.
Pankrat
1
une explication plus détaillée: dba.stackexchange.com/questions/27153/… il mentionne les implications du verrou exclusif, et quelques moyens de le contourner
John Douthat
4
Oui, la modification d'une table dans postgres attrape un verrou exclusif, mais comme l'opération elle-même se termine en millisecondes, cela n'a pratiquement pas d'importance dans la plupart des cas. J'ai personnellement ajouté des colonnes à des tables de cent millions de lignes au milieu de la journée ouvrable sans aucun temps d'arrêt.
Noah Yetter
2
@cobbzilla Oui, DROP COLUMN est tout aussi rapide. Sous le capot, il marque essentiellement la colonne comme cachée. Les valeurs qui existaient dans cette colonne avant d'être supprimées sont toujours dans les fichiers de données (et visibles pour les autres transactions) et le resteront à moins que et jusqu'à ce que vous effectuiez un VACUUM FULL.
Noah Yetter
7

Puisque vous avez posé des questions sur d'autres bases de données, voici quelques informations sur Oracle.

L'ajout d'une colonne NULL à une table Oracle est une opération très rapide car elle ne met à jour que le dictionnaire de données. Cela maintient un verrou exclusif sur la table pendant une très courte période de temps. Cependant, cela invalidera toutes les procédures stockées, vues, déclencheurs, etc. qui seront automatiquement recompilés.

De là, si nécessaire, vous pouvez créer un index à l'aide de la clause ONLINE. Encore une fois, seuls les verrous de dictionnaire de données très courts. Il lira le tableau entier à la recherche de choses à indexer, mais ne bloquera personne en faisant cela.

Si vous devez ajouter une clé étrangère, vous pouvez le faire et demander à Oracle de vous assurer que les données sont correctes. Sinon, il doit lire toute la table et valider toutes les valeurs qui peuvent être lentes (créez d'abord votre index).

Si vous avez besoin de mettre une valeur par défaut ou calculée dans chaque ligne de la nouvelle colonne, vous devrez exécuter une mise à jour massive ou peut-être un petit programme utilitaire qui remplit les nouvelles données. Cela peut être lent, surtout si les rangées deviennent beaucoup plus grandes et ne rentrent plus dans leurs blocs. Le verrouillage peut être géré pendant ce processus. Étant donné que l'ancienne version de votre application, qui est toujours en cours d'exécution, ne connaît pas cette colonne, vous pourriez avoir besoin d'un déclencheur sournois ou de spécifier une valeur par défaut.

À partir de là, vous pouvez faire un switcharoo sur vos serveurs d'applications vers la nouvelle version du code et il continuera à fonctionner. Lâchez votre gâchette sournoise.

Alternativement, vous pouvez utiliser DBMS_REDEFINITION qui est une boîte noire conçue pour faire ce genre de chose.

Tout cela est tellement embêtant à tester, etc. que nous avons juste une panne tôt le dimanche matin chaque fois que nous sortons une version majeure.

WW.
la source
3

Si vous ne pouvez pas vous permettre de temps d'arrêt pour votre base de données lors de la mise à jour des applications, vous devez envisager de maintenir un cluster à deux nœuds pour une haute disponibilité. Avec une configuration de réplication simple, vous pouvez effectuer des changements structurels presque entièrement en ligne comme celui que vous suggérez:

  • attendre que toutes les modifications soient répliquées sur un esclave passif
  • changer l'esclave passif en maître actif
  • faire les changements structurels de l'ancien maître
  • répliquer les modifications du nouveau maître vers l'ancien maître
  • refaire l'échange principal et le nouveau déploiement d'application simultanément

Ce n'est pas toujours facile mais ça marche, généralement avec 0 temps d'arrêt! Le deuxième nœud ne doit pas être uniquement passif, il peut être utilisé pour tester, faire des statistiques ou comme nœud de secours. Si vous n'avez pas d'infrastructure, la réplication peut être configurée sur une seule machine (avec deux instances de MySQL).

jynus
la source
1
L'ancien maître est hors du cluster ou dans le cluster?
John Chornelius
2

Nan. Si vous utilisez des tables MyISAM, à ma meilleure compréhension, ils ne font que des verrous de table - il n'y a pas de verrous d'enregistrement, ils essaient simplement de tout garder ultra-rapide grâce à la simplicité. (D'autres tables MySQL fonctionnent différemment.) Dans tous les cas, vous pouvez copier la table dans une autre table, la modifier, puis les changer, en mettant à jour les différences.

Il s'agit d'une modification si massive que je doute qu'un SGBD le supporte. C'est considéré comme un avantage de pouvoir le faire avec les données du tableau en premier lieu.

dkretz
la source
InnoDB utilise des verrous de ligne - dev.mysql.com/doc/refman/5.0/en/internal-locking.html
Eran Galperin
Ouais, MySQL est l'aberration. C'est pourquoi j'ai été précis sur les tableaux «standard».
dkretz
Vous avez écrit - les tables MySQL standard ne font que des verrous de table - ce qui est incorrect.
Eran Galperin
Comment interprétez-vous cela à propos des tables MyISAM (c'est-à-dire le standard MySQL) de la page que vous avez citée? "MySQL utilise le verrouillage au niveau de la table pour les tables MyISAM et MEMORY, le verrouillage au niveau de la page pour les tables BDB et le verrouillage au niveau de la ligne pour les tables InnoDB."
dkretz
certains moteurs de stockage utilisent le verrouillage au niveau des lignes et certains utilisent le verrouillage au niveau des tables. Il n'y a pas de moteur de stockage standard (peut-être que vous parliez de la valeur par défaut dans phpMyAdmin ...)
Eran Galperin
2

Solution temporaire...

Une autre solution pourrait être d'ajouter une autre table avec la clé primaire de la table d'origine, ainsi que votre nouvelle colonne.

Remplissez votre clé primaire sur la nouvelle table et remplissez les valeurs de la nouvelle colonne dans votre nouvelle table, et modifiez votre requête pour joindre cette table pour les opérations de sélection et vous devez également insérer, mettre à jour séparément pour cette valeur de colonne.

Lorsque vous parvenez à obtenir des temps d'arrêt, vous pouvez modifier la table d'origine, modifier vos requêtes DML et supprimer votre nouvelle table créée précédemment

Sinon, vous pouvez opter pour la méthode de clustering, la réplication, l'outil pt-online-schema de percona

Balasundaram
la source
1

En utilisant le plugin Innodb, les instructions ALTER TABLE qui ajoutent ou suppriment uniquement des index secondaires peuvent être faites "rapidement", c'est-à-dire sans reconstruire la table.

De manière générale cependant, dans MySQL, toute ALTER TABLE implique la reconstruction de la table entière, ce qui peut prendre un temps très long (c'est-à-dire si la table contient une quantité utile de données).

Vous devez vraiment concevoir votre application de manière à ce que les instructions ALTER TABLE n'aient pas à être effectuées régulièrement; vous ne voulez certainement pas qu'ALTER TABLE soit exécuté pendant l'exécution normale de l'application, sauf si vous êtes prêt à attendre ou que vous modifiez de minuscules tables.

MarkR
la source
1

Je recommanderais l'une des deux approches:

  1. Concevez vos tables de base de données en gardant à l'esprit les changements potentiels. Par exemple, j'ai travaillé avec des systèmes de gestion de contenu, qui modifient régulièrement les champs de données dans le contenu. Au lieu de créer la structure de la base de données physique pour répondre aux exigences initiales du champ CMS, il est préférable de créer une structure flexible. Dans ce cas, utilisez un champ de texte blob (varchar (max) par exemple) pour contenir des données XML flexibles. Cela rend les changements structurels très moins fréquents. Les changements structurels peuvent être coûteux, il y a donc un avantage à coûter ici aussi.

  2. Avoir le temps de maintenance du système. Soit le système se met hors ligne pendant les modifications (mensuelles, etc.), et les modifications sont planifiées à l'heure la moins fréquentée de la journée (3 à 5 heures du matin, par exemple). Les changements sont mis en place avant le déploiement de la production, vous aurez donc une bonne estimation de la fenêtre fixe des temps d'arrêt.

2a. Avoir des serveurs redondants, de sorte que lorsque le système a des temps d'arrêt, l'ensemble du site ne tombe pas en panne. Cela vous permettrait de "déployer" vos mises à jour de manière échelonnée, sans interrompre tout le site.

Les options 2 et 2a peuvent ne pas être réalisables; ils ont tendance à ne concerner que les sites / opérations plus importants. Ce sont des options valides, cependant, et j'ai personnellement utilisé toutes les options présentées ici.

pearcewg
la source
1

Si quelqu'un est encore en train de lire ceci ou arrive à venir ici, c'est le gros avantage de l'utilisation d'un système de base de données NoSQL comme mongodb. J'ai eu le même problème avec la modification de la table pour ajouter des colonnes pour des fonctionnalités supplémentaires ou des index sur une grande table avec des millions de lignes et des écritures élevées. Cela finirait par se bloquer pendant très longtemps, donc faire cela sur la base de données LIVE frustrerait nos utilisateurs. Sur de petites tables, vous pouvez vous en sortir.

Je déteste le fait que nous devions «concevoir nos tableaux pour éviter de les altérer». Je ne pense tout simplement pas que cela fonctionne dans le monde des sites Web d'aujourd'hui. Vous ne pouvez pas prédire comment les gens utiliseront votre logiciel, c'est pourquoi vous changez rapidement les choses en fonction des commentaires des utilisateurs. Avec mongodb, vous pouvez ajouter des "colonnes" à volonté sans temps d'arrêt. Vous ne les ajoutez même pas vraiment, vous insérez simplement des données avec de nouvelles colonnes et il le fait automatiquement.

Vaut le détour: www.mongodb.com

Brian Gruber
la source
2
MySQL est encore utilisé dans de nombreux systèmes, donc la question est vraiment de savoir comment réaliser un changement de schéma dans le SGBDR SQL, même si je suis aussi un fervent partisan de NoSQL.
Alexy
1

En général, la réponse sera «non». Vous modifiez la structure de la table, ce qui nécessitera potentiellement de nombreuses mises à jour "et je suis tout à fait d'accord avec cela. Si vous prévoyez de le faire souvent, je proposerai une alternative aux colonnes" factices "- utilisez VIEWplutôt s de tables pour SELECTles données. IIRC, la modification de la définition d'une vue est relativement légère et l'indirection à travers une vue est effectuée lorsque le plan de requête est compilé. La dépense est que vous auriez à ajouter la colonne à une nouvelle table et faire le vue JOINdans la colonne.

Bien sûr, cela ne fonctionne que si vous pouvez utiliser des clés étrangères pour effectuer une cascade de suppressions et autres. L'autre avantage est que vous pouvez créer une nouvelle table contenant une combinaison des données et y pointer la vue sans perturber l'utilisation du client.

Juste une pensée.

D.Shawley
la source
1

La différence entre Postgres et MySQL à cet égard est que dans Postgres, il ne recrée pas une table, mais modifie le dictionnaire de données qui est similaire à Oracle. Par conséquent, l'opération est rapide, alors qu'elle nécessite toujours d'allouer un verrou de table DDL exclusif pendant très peu de temps, comme indiqué ci-dessus par d'autres.

Dans MySQL, l'opération copiera les données dans une nouvelle table tout en bloquant les transactions, ce qui était le principal problème pour les DBA MySQL avant la version 5.6.

La bonne nouvelle est que depuis la sortie de MySQL 5.6, la restriction a été en grande partie levée et vous pouvez maintenant profiter de la véritable puissance de la base de données MYSQL.

Dmitriy Royzenberg
la source
3
Il semble que vous essayiez de créer un lien vers une référence concernant un changement dans MySql 5.6, mais cela n'a pas fonctionné. Veuillez réessayer.
dg99
1

Comme SeanDowney l'a mentionné, pt-online-schema-changec'est l'un des meilleurs outils pour faire ce que vous avez décrit dans la question ici. J'ai récemment fait beaucoup de changements de schéma sur une base de données en direct et cela s'est plutôt bien passé. Vous pouvez en savoir plus sur mon article de blog ici: http://mrafayaleem.com/2016/02/08/live-mysql-schema-changes-with-percona/ .

Rafay
la source
1

Vous devriez certainement essayer pt-online-schema-change. J'utilise cet outil pour effectuer des migrations sur AWS RDS avec plusieurs esclaves et cela a très bien fonctionné pour moi. J'ai écrit un article de blog élaboré sur la façon de faire ce qui pourrait vous être utile.

Blog: http://mrafayaleem.com/2016/02/08/live-mysql-schema-changes-with-percona/

Rafay
la source
0

Les colonnes factices sont une bonne idée si vous pouvez prédire leur type (et les rendre nulles). Vérifiez comment votre moteur de stockage gère les valeurs nulles.

MyISAM verrouille tout si vous mentionnez même un nom de table au passage, au téléphone, à l'aéroport. Il fait juste ça ...

Cela étant dit, les serrures ne sont pas vraiment un problème; tant que vous n'essayez pas d'ajouter une valeur par défaut pour la nouvelle colonne à chaque ligne, mais laissez-la reposer comme nulle, et que votre moteur de stockage est suffisamment intelligent pour ne pas l'écrire, vous devriez accepter un verrou qui n'est que tenu suffisamment longtemps pour mettre à jour les métadonnées. Si vous essayez d'écrire une nouvelle valeur, eh bien, vous portez un toast.

SquareCog
la source
1
J'ai essayé d'ajouter une colonne NULL à une table InnoDB et il a fallu reconstruire la table entière; pas une simple opération de «mise à jour des métadonnées».
Daniel
Je pense que l'idée était d'inclure des colonnes supplémentaires, nullables, dans la base de données lors de sa conception, de sorte que si une nouvelle fonctionnalité est requise, on puisse "ajouter" une nouvelle colonne simplement en commençant à l'utiliser. Il n'aura pas un joli nom, mais si le type de données a été correctement choisi / prédit, cela devrait fonctionner.
supercat
0

TokuDB peut ajouter / supprimer des colonnes et ajouter des index "à chaud", la table est entièrement disponible tout au long du processus. Il est disponible via www.tokutek.com

tmcallaghan
la source
-6

Pas vraiment.

Vous modifiez la structure sous-jacente de la table, après tout, et c'est un peu d'information qui est assez importante pour le système sous-jacent. Vous déplacez également (probablement) une grande partie des données sur le disque.

Si vous prévoyez de faire beaucoup cela, il vaut mieux simplement remplir le tableau avec des colonnes «factices» qui sont disponibles pour une utilisation future.

Will Hartung
la source
3
Remplir une table avec des colonnes factices semble être une très mauvaise idée.
Jost