Créer un index sur une énorme table de production MySQL sans verrouillage de table

104

J'ai besoin de créer un index sur une table MySQL d'environ 5 millions de lignes. C'est une table de production, et je crains un bloc complet de tout si j'exécute une instruction CREATE INDEX ...

Existe-t-il un moyen de créer cet index sans bloquer les insertions et les sélections?

Je me demande simplement que je ne dois pas m'arrêter, créer un index et redémarrer mon système!

n0cturne
la source
1
assurez-vous que vos paramètres myisam_sort_buffer_size et myisam_max_sort_file_size sont suffisamment grands.
Jon Black

Réponses:

130

[2017] Mise à jour: MySQL 5.6 prend en charge les mises à jour d'index en ligne

https://dev.mysql.com/doc/refman/8.0/en/innodb-online-ddl-operations.html#online-ddl-index-syntax-notes

Dans MySQL 5.6 et versions ultérieures, la table reste disponible pour les opérations de lecture et d'écriture pendant la création ou la suppression de l'index. L'instruction CREATE INDEX ou DROP INDEX ne se termine qu'après que toutes les transactions qui accèdent à la table sont terminées, de sorte que l'état initial de l'index reflète le contenu le plus récent de la table. Auparavant, la modification de la table pendant la création ou la suppression d'un index entraînait généralement un blocage qui annulait l'instruction INSERT, UPDATE ou DELETE sur la table.

[2015] La mise à jour de la table indique les écritures de blocs dans MySQL 5.5

D'après la réponse ci-dessus:

"Si vous utilisez une version supérieure à 5.1, les index sont créés alors que la base de données est en ligne. Ne vous inquiétez donc pas, vous n'interrompez pas l'utilisation du système de production."

C'est **** FALSE **** (du moins pour les tables MyISAM / InnoDB, ce que 99,999% des gens utilisent. L'édition en cluster est différente.)

Faire des opérations UPDATE sur une table bloquera pendant la création de l'index. MySQL est vraiment, vraiment stupide à ce sujet (et quelques autres choses).

Script de test:

(   
  for n in {1..50}; do
    #(time mysql -uroot -e 'select  * from website_development.users where id = 41225\G'>/dev/null) 2>&1 | grep real;
    (time mysql -uroot -e 'update website_development.users set bio="" where id = 41225\G'>/dev/null) 2>&1 | grep real;
  done
) | cat -n &
PID=$!
sleep 0.05
echo "Index Update - START"
mysql -uroot website_development -e 'alter table users add index ddopsonfu (last_name, email, first_name, confirmation_token, current_sign_in_ip);'
echo "Index Update - FINISH"
sleep 0.05
kill $PID
time mysql -uroot website_development -e 'drop index ddopsonfu on users;'

Mon serveur (InnoDB):

Server version: 5.5.25a Source distribution

Sortie (notez comment la 6ème opération se bloque pendant les ~ 400 ms nécessaires pour terminer la mise à jour de l'index):

 1  real    0m0.009s
 2  real    0m0.009s
 3  real    0m0.009s
 4  real    0m0.012s
 5  real    0m0.009s
Index Update - START
Index Update - FINISH
 6  real    0m0.388s
 7  real    0m0.009s
 8  real    0m0.009s
 9  real    0m0.009s
10  real    0m0.009s
11  real    0m0.009s

Vs lire les opérations qui ne bloquent pas (permuter le commentaire de ligne dans le script):

 1  real    0m0.010s
 2  real    0m0.009s
 3  real    0m0.009s
 4  real    0m0.010s
 5  real    0m0.009s
Index Update - START
 6  real    0m0.010s
 7  real    0m0.010s
 8  real    0m0.011s
 9  real    0m0.010s
...
41  real    0m0.009s
42  real    0m0.010s
43  real    0m0.009s
Index Update - FINISH
44  real    0m0.012s
45  real    0m0.009s
46  real    0m0.009s
47  real    0m0.010s
48  real    0m0.009s

Mettre à jour le schéma de MySQL sans temps d'arrêt

Ainsi, je ne connais qu'une seule méthode pour mettre à jour un schéma MySql et ne pas subir de panne de disponibilité. Maîtres circulaires:

  • Master A a votre base de données MySQL en cours d'exécution
  • Mettez le maître B en service et faites-le répliquer les écritures du maître A (B est un esclave de A)
  • Effectuez la mise à jour du schéma sur Master B.Il prendra du retard lors de la mise à niveau
  • Laissez Maître B vous rattraper. Invariant: votre changement de schéma DOIT être capable de traiter les commandes répliquées à partir d'un schéma de downversion. Les changements d'indexation sont éligibles. Les ajouts de colonnes simples sont généralement admissibles. Supprimer une colonne? probablement pas.
  • Échanger ATOMIQUEMENT tous les clients du maître A au maître B.Si vous voulez être en sécurité (faites-moi confiance, vous le faites), vous devez vous assurer que la dernière écriture sur A est répliquée sur B AVANTB prend sa première écriture. Si vous autorisez des écritures simultanées vers plus de 2 maîtres, ... vous comprendrez mieux la réplication MySQL à un niveau DEEP ou vous vous dirigez vers un monde de douleur. Douleur extrême. Comme, avez-vous une colonne qui est AUTOINCREMENT ??? vous êtes foutu (sauf si vous utilisez des nombres pairs sur un maître et des cotes sur l'autre). Ne faites PAS confiance à la réplication MySQL pour "faire ce qu'il faut". Ce n'est PAS intelligent et ne vous sauvera pas. C'est juste un peu moins sûr que de copier les journaux de transactions binaires à partir de la ligne de commande et de les relire à la main. Néanmoins, déconnecter tous les clients de l'ancien maître et les basculer vers le nouveau maître peut être effectué en quelques secondes, beaucoup plus rapidement que d'attendre une mise à niveau du schéma de plusieurs heures.
  • Maintenant, Master B est votre nouveau maître. Vous avez le nouveau schéma. La vie est belle. Prenez une bière; le pire est passé.
  • Répétez le processus avec le maître A, en mettant à niveau son schéma afin qu'il devienne votre nouveau maître secondaire, prêt à prendre le relais au cas où votre maître principal (maître B maintenant) perdrait le pouvoir ou tout simplement en place et mourrait sur vous.

Ce n'est pas un moyen simple de mettre à jour le schéma. Réalisable dans un environnement de production sérieux; Oui, ça l'est. S'il vous plaît, s'il vous plaît, s'il existe un moyen plus simple d'ajouter un index à une table MySQL sans bloquer les écritures, faites le moi savoir.

Googler m'amène à cet article qui décrit une technique similaire. Mieux encore, ils conseillent de boire au même moment de la procédure (notez que j'ai écrit ma réponse avant de lire l'article)!

Changement de schéma pt-en-ligne de Percona

L' article que j'ai lié ci-dessus parle d'un outil, pt-online-schema-change , qui fonctionne comme suit:

  • Créez une nouvelle table avec la même structure que l'original.
  • Mettre à jour le schéma sur une nouvelle table.
  • Ajouter un déclencheur sur la table d'origine afin que les modifications soient synchronisées avec la copie
  • Copiez les lignes par lots de la table d'origine.
  • Retirez la table d'origine et remplacez-la par une nouvelle table.
  • Déposez l'ancienne table.

Je n'ai jamais essayé l'outil moi-même. YMMV

RDS

J'utilise actuellement MySQL via le RDS d'Amazon . C'est un service vraiment astucieux qui encapsule et gère MySQL, vous permettant d'ajouter de nouvelles répliques en lecture avec un seul bouton et de mettre à niveau de manière transparente la base de données à travers les SKU matériels. C'est vraiment pratique. Vous n'obtenez pas un accès SUPER à la base de données, vous ne pouvez donc pas visser directement avec la réplication (est-ce une bénédiction ou une malédiction?). Cependant, vous pouvez utiliser la promotion de réplique en lecture pour apporter des modifications de schéma sur un esclave en lecture seule, puis promouvoir cet esclave pour qu'il devienne votre nouveau maître. Exactement la même astuce que j'ai décrite ci-dessus, juste beaucoup plus facile à exécuter. Ils ne font toujours pas grand-chose pour vous aider avec la transition. Vous devez reconfigurer et redémarrer votre application.

Dave Dopson
la source
3
pt-online-schema-change fonctionne très bien même dans une réplication maître-esclave. Je l'ai utilisé pour effectuer une migration en direct sur une table de lecture occupée de plus de 20 millions d'enregistrements sur notre base de données principale de production avec 2 esclaves de réplication sans aucun problème ni temps d'arrêt. La préparation du script prend du temps et je dois généralement créer un fichier .sql contenant le changement SQL brut et un fichier .sh comme wrapper pour exécuter le même SQL mais au format fragment (pas d'ALTER TABLE). Vous pouvez exécuter plusieurs commandes avec pt-online-schema-change en les enchaînant et séparées par une virgule.
Alex Le
-1; Je ne connais pas les anciennes versions, mais je sais que la création d'index ne bloque pas le DML simultané dans MySQL 5.6+ (pour lequel un RC existait au moment où cette réponse a été écrite, et qui avait été officiellement publié lorsque cette réponse a duré édité en mai 2013) parce que je me suis appuyé sur cela pour exécuter des créations d'index de plusieurs heures sur des tables de production tout en acceptant les insertions. Et bien que vous ayez peut- être raison sur le blocage de la création d'index DML dans la version 5.5 et les versions antérieures, le délai de moins d'une seconde démontré ici n'est pas entièrement convaincant.
Mark Amery
@MarkAmery - un comportement de blocage est un comportement de blocage, et 400 ms est une éternité. Blocs MySQL 5.5 pour les mises à jour d'index. Créez une base de données de test plus grande et elle bloquera pendant des secondes, des heures ou des jours. J'ai écrit cet article avant que MySQL 5.6 n'ait des mises à jour de schéma en ligne, donc mon contenu original ne reflète pas ce fait. J'ai mis à jour le message pour refléter les informations nouvellement disponibles.
Dave Dopson
@DaveDopson, êtes-vous sûr à 100% que seules les opérations UPDATE sont bloquées?
toto_tico
C'était le cas de la version que j'ai testée.
Dave Dopson
67

Comme le souligne cet article de blog , l'InnoDBALTER TABLE mécanisme a été complètement repensé pour MySQL 5.6.

(Pour un aperçu exclusif de ce sujet, la documentation MySQL peut fournir un après-midi de lecture.)

Pour ajouter un index à une table sans verrou résultant sur UPDATE/ INSERT, le format d'instruction suivant peut être utilisé:

ALTER TABLE my_table ADD INDEX my_table__idx (my_column), ALGORITHM=INPLACE, LOCK=NONE;
A dessiné
la source
4
Mise en garde
Alexander Torstling
16

Mise à jour MySQL 5.6 (février 2013): Vous pouvez désormais effectuer des opérations de lecture et d'écriture pendant la création d'un index, même avec des tables InnoDB - http://dev.mysql.com/doc/refman/5.6/en/innodb-create-index -overview.html

Dans MySQL 5.6 et versions ultérieures, la table reste disponible pour les opérations de lecture et d'écriture pendant la création ou la suppression de l'index. L'instruction CREATE INDEX ou DROP INDEX ne se termine qu'après que toutes les transactions qui accèdent à la table sont terminées, de sorte que l'état initial de l'index reflète le contenu le plus récent de la table. Auparavant, la modification de la table pendant la création ou la suppression d'un index entraînait généralement un blocage qui annulait l'instruction INSERT, UPDATE ou DELETE sur la table.

et:

Dans MySQL 5.6, cette fonctionnalité devient plus générale: vous pouvez lire et écrire dans des tables pendant la création d'un index, et de nombreux autres types d'opérations ALTER TABLE peuvent être effectuées sans copier la table, sans bloquer les opérations DML, ou les deux. Ainsi, dans MySQL 5.6 et supérieur, nous appelons généralement cet ensemble de fonctionnalités DDL en ligne plutôt que création d'index rapide.

depuis http://dev.mysql.com/doc/refman/5.6/en/glossary.html#glos_fast_index_creation

Eric Saboia
la source
Alors, comment expliquer l'analyse de Dave?
Nikhil Sahu
1
@NikhilSahu Dave ne testait clairement pas sur MySQL 5.6, mais sur une version plus ancienne. Notez que la version 5.6 n'était pas encore publiée au moment où Dave a publié la révision initiale de sa réponse.
Mark Amery
+1. Mon analyse était sur MySQL 5.5 (le dernier qui était disponible en 2013). Je mets à jour ma réponse pour refléter les nouvelles fonctionnalités de MySQL 5.6.
Dave Dopson
3

pt-online-schema-change est la voie à suivre si vous voulez vraiment vous assurer que la migration ne fera pas tomber le site.

Comme je l'ai écrit dans le commentaire ci-dessus, j'ai plusieurs expériences avec pt-online-schema-change en production. Nous avons notre table principale de 20M + enregistrements et un maître -> 2 esclaves de réplication en lecture seule. J'ai effectué au moins des dizaines de migrations avec pt-online-schema-change, de l'ajout d'une nouvelle colonne, du changement de charset à l'ajout de plusieurs index. Nous desservons également des tonnes de trafic pendant la période de migration et nous n'avons eu aucun hoquet. Bien sûr, vous devrez tester tous les scripts de manière approfondie avant de passer en production.

J'ai essayé de regrouper les modifications en 1 script afin que pt-online-schema-change n'ait à copier les données qu'une seule fois. Et soyez très prudent avec le changement de nom de colonne car vous perdrez vos données. Cependant, l'ajout d'un index devrait convenir.

Alex Le
la source
Je ne suis pas d'accord avec votre recommandation sans réserve de pt-online-schema-change. C'est génial, mais c'est exagéré pour de nombreuses situations où les capacités DDL en ligne de MySQL 5.6 + fonctionnent déjà correctement. Il a également des limitations (comme ne pas jouer correctement avec les déclencheurs) et double la quantité d'écriture nécessaire par insertion dans la table d'origine pendant qu'un changement de schéma est en cours. Cela imposera beaucoup plus à votre disque qu'un changement de schéma en ligne ordinaire, et a donc le potentiel de «faire tomber votre site» dans des circonstances où le simple fait d'exécuter le changement de schéma de manière simple aurait bien fonctionné.
Mark Amery
J'ai écrit sur la base de mon expérience réelle avec pt-online-schema-change à l'époque, donc je ne sais pas pourquoi vous qualifieriez ma recommandation de «non qualifiée». Nous avions au moins plus de 1000 visiteurs sur le site à un moment donné lorsque j'ai exécuté les changements de schéma, et bien sûr, le disque IO était pénible, mais notre site n'a pas été interrompu. Une bonne mise en cache a également aidé. Je n'ai pas utilisé le DDL en ligne MySQL 5.6+ mais d'après mon expérience, pt-online-schema-change a bien fait son travail dans notre cas.
Alex Le
1
@AlexYe Yikes, je voulais dire "sans réserve" dans le sens de "sans réserve" plutôt que dans le sens de "livré par quelqu'un qui n'est pas qualifié pour commenter" - cette dernière interprétation ne m'est pas venue jusqu'à ce que j'ai vu votre commentaire et n'est certainement pas c'est pas ce que je voulais! c'est-à-dire que je disais que si pt-online-schema-changec'est un outil utile, il y a de très nombreuses situations dans lesquelles le DDL en ligne ordinaire est tout aussi bon et une poignée là où c'est meilleur, donc toute recommandation devrait être soigneusement mise en garde plutôt qu'universelle.
Mark Amery