Comment ajouter une colonne à une grande table dans MySQL


13

Je suis développeur PHP, alors ne soyez pas strict. J'ai une grande table ~ 5.5 Go de vidage. Notre PM a décidé de faire une nouvelle colonne pour effectuer une nouvelle fonctionnalité. La table est InnoDB donc ce que j'ai essayé:

  1. Modifier la table en écran avec verrou de table. A pris environ 30 heures et rien. Alors je l'ai juste arrêté. J'ai d'abord fait une erreur parce que je n'ai pas mis fin à toutes les transactions, mais la deuxième fois, il n'y a pas eu de verrouillage multiple. Le statut était copy to tmp table.

  2. Puisque j'ai également besoin d'appliquer le partitionnement pour cette table, nous décidons de faire un vidage, de renommer et de créer une table avec le même nom et une nouvelle structure. Mais le vidage fait une copie stricte (au moins je n'ai pas trouvé autre chose). J'ai donc ajouté pour vider une nouvelle colonne avec sedet l'interroger. Mais d'étranges erreurs ont commencé. Je crois que cela a été causé par charset. La table dans utf-8 et le fichier sont devenus us-ascii après sed. J'ai donc eu des erreurs (commande inconnue '\' ') sur 30% des données. C'est donc aussi une mauvaise façon.

Quelles sont les autres options pour accomplir cela et accélérer les performances (je peux le faire avec un script php, mais cela prendra des siècles). Quelle sera la performance de INSERT SELECTdans ce cas.

Merci pour toute avance.

Réponses:


12

Utilisez MySQL Workbench . Vous pouvez cliquer avec le bouton droit sur une table et sélectionner "Envoyer à l'éditeur SQL" -> "Créer une instruction". De cette façon, aucune "propriété" de table ne sera oubliée (y compris CHARSETou COLLATE).
Avec cette énorme quantité de données, je recommanderais de nettoyer la table ou la structure de données que vous utilisez (un bon DBA est pratique). Si ce n'est pas possible:

  • renommer la table ( ALTER) et en créer une nouvelle avec le CREATEscript que vous obtenez à partir de Workbench. Vous pouvez également étendre cette requête avec le nouveau champ dont vous avez besoin
  • LOAD BULK les données de l'ancienne table vers la nouvelle:
    SET FOREIGN_KEY_CHECKS = 0;
    SET UNIQUE_CHECKS = 0;
    SET AUTOCOMMIT = 0;
    INSERT INTO new_table (fieldA, fieldB, fieldC, ..., fieldN)
       SELECT fieldA, fieldB, fieldC, ..., fieldN
       FROM old_table
    SET UNIQUE_CHECKS = 1;
    SET FOREIGN_KEY_CHECKS = 1;
    COMMIT;
    

    De cette façon, vous évitez d'indexer / etc pour exécuter enregistrement par enregistrement. La "mise à jour" du tableau sera encore lente (car la quantité de données est énorme) mais c'est le moyen le plus rapide auquel je peux penser.

    EDIT: lisez cet article pour obtenir des détails sur les commandes utilisées dans l'exemple de requête ci-dessus;)

Mes options vont bien. Et j'ai eu SET NAMES utf8et. COLLATIONMais meh idk pourquoi 30% des données sont corrompues après sed. Je pense que la charge en vrac sera la plus rapide mais peut-être que quelque chose de plus existe qui me manque. Merci Mark
ineersa

1
La corruption des données @ineersa peut avoir plusieurs raisons: par exemple, vous avez ouvert le fichier avec un éditeur qui ne prend pas en charge tous les caractères et vous l'avez enregistré. Ou bien, la façon dont vous essayez d'importer depuis le vidage corrompt les données (c'est bogué et ne peut pas lire le fichier correctement). Ou, le même type peut identifier une partie de certaines données comme une expression (par exemple "james \ robin" == "\ r" comme expression), etc. C'est pourquoi je ne recommande jamais d'utiliser le vidage, pas même avec l'outil de vidage de données binaires uniquement, pas même avec dev.mysql.com/doc/refman/5.6/en/mysqldump.html (ou BCP pour MS SQL Server). Cela se passe bien trop de fois ...

yeap j'ai essayé avec hex-blob. ça n'aide pas. Aussi, juste après avoir utilisé sed mysql identifier \ 'comme commande dans certains noms (pas dans tous). C'est étrange et buggy. J'essaierai la charge en vrac ce soir. J'espère que cela se fera au moins dans 10 à 15 heures.
ineersa

@ineersa espère que ce sera le cas. vous pouvez également essayer d'ajouter seulement une partie des données, disons 10% pour voir combien de temps cela prend - et avoir une estimation pour l'ensemble de la transaction. Cependant, ce sera une estimation très approximative, les choses peuvent ralentir si les caches / mémoire / tout ce qui est rempli / surchargé.

1
Merci Mark. A travaillé génial. Encore plus rapide que la restauration depuis le vidage. A pris environ 5 heures.
ineersa

5

Votre idée sed est une méthode décente, mais sans les erreurs ou la commande que vous avez exécutée, nous ne pouvons pas vous aider.

Cependant, une méthode bien connue pour apporter des modifications en ligne à de grandes tables est pt-online-schema-change . L'oubli simpliste de ce que fait cet outil est copié à partir de la documentation:

pt-online-schema-change fonctionne en créant une copie vide de la table à modifier, en la modifiant comme vous le souhaitez, puis en copiant les lignes de la table d'origine dans la nouvelle table. Une fois la copie terminée, elle éloigne la table d'origine et la remplace par la nouvelle. Par défaut, il supprime également la table d'origine.

Cette méthode peut également prendre un certain temps, mais pendant le processus, la table d'origine sera complètement utilisable.


J'essaierai le chargement en masse plus tard ce soir. Si cela ne fonctionne pas, vous aurez probablement besoin de cet outil. Des erreurs sont provoquées par la suppression de certains symboles après avoir utilisé sed comme commandes. Par exemple 'D\'agostini', provoquera une erreur unknown command '\''. Mais pas toujours, comme dans 30% des cas. C'est étrange et buggy. La même chose vient même avec les vidages hex-blob. Merci Derek.
ineersa

4

alter table add column, algorithm=inplace, lock=none modifiera une table MySQL 5.6 sans copier la table et sans impact de verrouillage.

Je viens de le tester hier, une masse a inséré 70 000 lignes dans une table de partition à 280 000 lignes 7, 10 000 lignes dans chaque partition, avec 5 secondes de sommeil entre les deux pour permettre un autre débit.

A commencé les insertions en masse, puis dans une session distincte a commencé la alterdéclaration en ligne ci-dessus dans MySQL Workbench, la alterfin avant les insertions, deux nouvelles colonnes ont été ajoutées, et aucune ligne n'a résulté de la modification, ce qui signifie que MySQL n'a copié aucune ligne.


1
Pourquoi cette réponse n'obtient-elle pas plus de votes?, Ne fonctionne-t-elle pas?
fguillen

1

Actuellement, la meilleure option pour modifier des tables énormes est probablement https://github.com/github/gh-ost

gh-ost est une solution de migration de schéma en ligne sans déclencheur pour MySQL. Il est testable et offre une pause, un contrôle / reconfiguration dynamique, un audit et de nombreux avantages opérationnels.

gh-ost génère une charge de travail légère sur le maître tout au long de la migration, découplée de la charge de travail existante sur la table migrée.

Il a été conçu sur la base d'années d'expérience avec les solutions existantes et change le paradigme des migrations de tables.


1

Je pense que Mydumper / Myloader est un bon outil pour des opérations comme celle-ci: s'améliore chaque jour. Vous pouvez utiliser vos processeurs et charger des données en parallèle: http://www.percona.com/blog/2014/03/10/new-mydumper-0-6-1-release-offers-several-performance-and- fonctionnalités d'utilisation /

J'ai réussi à charger des centaines de gigaoctets de tables MySQL en quelques heures.

Maintenant, quand il s'agit d'ajouter une nouvelle colonne, c'est difficile car MySQL copie la table entière dans la TMPzone de mémoire avec ALTER TABLE...Bien que MySQL 5.6 dise qu'il peut faire des changements de schéma en ligne, je n'ai pas réussi à les faire en ligne pour des tables massives sans verrou contention encore.


-2

j'ai juste eu le même problème. Un petit contournement:

CREATE TABLE new_table SELECT * FROM oldtable;

SUPPRIMER DE new_table

ALTER TABLE new_table ADD COLUMN new_column int (11);

INSERT INTO new_table select *, 0 from old_table

drop table old_table; renommer la table new_table TO old_table;


Pourquoi ne pas simplement ajouter une clause where à l'instruction create table afin qu'elle ne sélectionne aucune donnée? Il serait également plus efficace de tronquer la table que de supprimer les données
Joe W

pourquoi supprimer, quand devoir insérer plus tard, à nouveau. Peut définir default = 0 à ADD COLUMN lui-même.
user195280
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.