web-dev-qa-db-fra.com

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

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 blocage complet de tout si je lance 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 juste de ne pas m'arrêter, créer un index et redémarrer mon système!

88
n0cturnal

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

https://dev.mysql.com/doc/refman/5.6/en/innodb-create-index-overview.html

Dans MySQL 5.6 et les 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'une fois 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 lors de la création ou de la suppression d'un index entraînait généralement un blocage qui annulait l'instruction INSERT, UPDATE ou DELETE de la table.

[2015] La mise à jour des indicateurs de table indique les blocs écrits dans MySQL 5.5

De la réponse ci-dessus:

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

C'est faux**** (du moins pour les tables MyISAM/InnoDB, utilisées par 99,999% des utilisateurs. L’édition en cluster est différente.)

Effectuer des opérations UPDATE sur une table va [~ # ~] bloquer [~ # ~] 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 (remarquez comment la 6ème opération bloque pendant environ 400 ms 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 (permutez 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

Mise à jour du schéma de MySQL sans temps d'arrêt

Ainsi, il n'y a qu'une seule méthode que je connaisse 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 sur elle
  • Mettre le maître B en service et le faire répliquer à partir du maître A (B est un esclave de A)
  • Effectuez la mise à jour du schéma sur le maître B. Elle prendra du retard lors de la mise à niveau.
  • Laisser le Maître B rattraper son retard. Invariant: votre modification de schéma DOIT être capable de traiter les commandes répliquées à partir d'un schéma de version inférieure. Les modifications d'indexation sont admissibles. Les ajouts de colonne simples sont généralement admissibles. Enlever une colonne? probablement pas.
  • ÉCHANGEZ TOTALEMENT tous les clients du Maître A au Maître B. Si vous voulez être en sécurité (faites-moi confiance), vous devez vous assurer que la dernière écriture sur A est répliquée sur B [~ # ~] avant [~ # ~] B prend sa première écriture. Si vous autorisez des écritures simultanées sur plus de 2 maîtres, vous comprendrez mieux la réplication MySQL à un niveau DEEP ou vous vous dirigez vers un monde douloureux. 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 probabilités sur l'autre). Ne faites PAS confiance à la réplication MySQL pour "faire le bon choix". Ce n'est pas intelligent et ne vous sauvera pas. C’est un peu moins sûr que de copier les journaux de transactions binaires à partir de la ligne de commande et de les rejouer à 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, bien plus rapidement que d'attendre une mise à niveau du schéma sur plusieurs heures.
  • Maintenant, le Maître B est votre nouveau maître. Vous avez le nouveau schéma. La vie est belle. Prendre 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 si votre maître principal (maître B maintenant) perd le pouvoir ou meurt et meurt.

Un moyen simple de mettre à jour le schéma n'est pas le cas. 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 y a un moyen plus facile d'ajouter un index à une table MySQL sans bloquer les écritures, faites le moi savoir.

Googler m'a conduit à 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)!

Le changement de schéma en ligne de Percona

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

  • Créer une nouvelle table avec la même structure que l'original.
  • Mettre à jour le schéma sur la nouvelle table.
  • Ajouter un déclencheur sur la table d'origine afin que les modifications restent synchronisées avec la copie
  • Copier des lignes par lots à partir de la table d'origine.
  • Éloignez la table d'origine et remplacez-la par une nouvelle.
  • Dépose 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 nouveaux réplicas en lecture avec un seul bouton et de mettre à niveau de manière transparente la base de données sur des SKU de matériel. C'est vraiment pratique. Vous n’obtenez pas un SUPER accès à 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 Promotion sur les répliques en lecture pour apporter les modifications de schéma sur un esclave en lecture seule, puis promouvoir cet esclave pour qu'il devienne votre nouveau maître. Exactement le même truc que je viens de décrire, mais beaucoup plus facile à exécuter. Ils ne font toujours pas grand chose pour vous aider avec le cut-over. Vous devez reconfigurer et redémarrer votre application.

111
Dave Dopson

Comme cela article de blog , le mécanisme InnoDB ALTER TABLE A été complètement repensé pour MySQL 5.6.

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

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;
57
Drew

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 les tables InnoDB - http://dev.mysql.com/doc/refman/5.6/en/innodb -create-index-overview.html

Dans MySQL 5.6 et les 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'une fois 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 lors de la création ou de la suppression d'un index entraînait généralement un blocage qui annulait l'instruction INSERT, UPDATE ou DELETE de la table.

et:

Dans MySQL 5.6, cette fonctionnalité devient plus générale: vous pouvez lire et écrire dans les tables pendant la création d’un index, et de nombreux autres types d’opérations ALTER TABLE peuvent être exécutés 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 le DDL en ligne plutôt que la création d'index rapide.

à partir de http://dev.mysql.com/doc/refman/5.6/fr/glossary.html#glos_fast_index_creation

14
Eric Saboia

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 une douzaine de migrations avec pt-online-schema-change, depuis l'ajout d'une nouvelle colonne, la modification du jeu de caractères, à l'ajout de plusieurs index. Nous servons aussi des tonnes de trafic pendant la période de migration et nous n’avons eu aucun problème. Bien sûr, vous devez tester tous les scripts de manière approfondie avant de l’utiliser en production.

J'ai essayé de regrouper les modifications dans 1 script afin que pt-online-schema-change ne copie qu'une seule fois les données. 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 bien se passer.

3
Alex Le