Comment recouvrer des données MySQL InnoDB après un crash ?
Le propos de cet article est d’aider un administrateur système à recouvrer des données live de production dans des tables au format InnoDB, d’une base de données MySQL qui repose sur un serveur Debian endommagé, par exemple à la suite d’un crash du disque qui porte le système.
Evidemment chaque crash a ses propres causes et ses conséquences, qui peuvent être bien différentes d’un cas à l’autre. Je ne peux ici relater que celui dont j’ai été témoin. Toutefois la méthode de greffe de fichiers InnoDB d’un serveur Linux Debian à l’autre pourra vous intéresser. Le problème principale est que les fichiers binaires qui renferment les données InnoDB ne sont pas directement exploitables après un simple copier-coller sur un serveur sain. Nous allons voir comment résoudre ce problème.
Le contexte
- "Qu’est-ce qu’on a ?", demande le chirurgien urgentiste.
- "Serveur endommagé."
Il s’agit ici d’un serveur Linux Debian. Mais le principe reste le même pour les autres moutures, sauf peut-être la récupération du mot de passe MySQL stocké ici dans /etc/mysql/debian.cnf.
Endommagé dans le sens où un crash disque d’un des disques du RAID rend impossible l’exécution correcte de l’OS, ou tout simplement l’exécution normal de mysqldump, ce qui empêche de migrer les données depuis ce serveur endommagé vers un failover (= un serveur sain qui prendra le relai).
Dommage, car on se rend compte que les fichiers du répertoire datadir1 sont lisibles car la partition qui les porte est montable grâce à une connexion physique du disque sur le failover.
1datadir désigne le répertoire paramétré dans le fichier de configuration mysql, visible par exemple par la commande :
grep datadir /etc/mysql/my.cnf
La situation actuelle est celle où un serveur failover a repris le service, mais avec des données non à jour, car issues du dernier backup. C’est pas mal, mais c’est pas idéal, car on fait sans les données entre la date du dernier backup et la date du crash. Ce sont ces données que l’on va tenter de récupérer…
En tout premier lieu, lors d’un crash et d’un failover (= une reprise de service sur un autre système),
il faut tout de suite“pousser les taquets”, une expression personnelle qui signifie que vous devrez, sur la machine de failover, placer des index AUTO_INCREMENT à des valeurs supérieures à ce que vous pensez être les valeurs des index lors du crash.
Prenez des valeurs rondes, arrondies à la valeur supérieur au sein du même ordre de grandeur. Prévoyez comem choix de cette valeur ronde une valeur qui laisse suffisamment de place pour les insertions que vous allez faire lors de la récupération des données. Je vous rappelle que les seules insertions qui manquent sont celles entre le dernier backup et la date du crash, puisque votre failover, à ce stade de la procédure, ne peut reprendre que les données du dernier backup, le seul disponible à ce stade.
Par exemple, supposons que vous en êtes à pousser les taquets d’une table nommée commande dont l’id lors du crash était autour de 25000, le 15 septembre à 17h30, et votre dernier backup connu contient toutes les données jusqu’au 15 septembre à 3h00. Alors, avant de commissionner votre failover (= avant de l’ouvrir au service, dans l’urgence, je sais…), placez un index d’AUTO_INCREMENT à 30000, pour "être tranquille".
Ainsi vous vous allouez un certain espace pour réinjecter tranquillement la différence, c’est à dire les données de la journée du crash : entre le 15 septembre 3h00 et 17h30.
Evidemment, à ce stade la continuité des numéros de commandes est un souci mineur, vous en conviendrez…
Rappel
Si classiquement vous avez des tables MyISAM, les données sont portées par les fichiers dont le nom est celui des tables.
Par contre, en format InnoDB, les .frm ne comportent que des informations structurelles, et non de données. Les données sont intimement codées dans les fichiers binaires ib*.
Cet article fait suite à un cas réel d’une machine de production endommagée suite à un accident non recouvrable sur le RAID :
la BDD fonctionnait sur une ancienne machine, le répertoire des données mysql (le citer) était supposé intègre,
mais un grave problème de RAID empêchait l’exécution normale de l’OS, alors que les données MySQL mêmes étaient supposées non-corrompues.
Quand une base est endommagée, on ne sait pas forcément ce qui est corrompu et ce qui ne l’est pas.
Dans la situation que j’ai connue, j’étais assez confiant que les fichiers MySQL eux-mêmes n’étaient pas corrompus, car portés par un disque du système RAID sain.
Ils étaient toujours lisibles, par chance, grâce à un montage d’un des disque du système RAID sur une machine de restauration.
Mais comme MySQL sur Debian repose en partie sur une logique de permissions/protection intrinsèques,
le répertoire de données ne peux pas être grossièrement copié (ex. tar) en espérant que les données soient lues sur la machine de production failover.
Voici donc une procédure de recouvrement des données qui m’a donné satisfaction.
Conscient que votre incident d’aujourd’hui n’est pas forcément identique, je me suis efforcé de préciser le contexte pour que vous preniez la décision de continuer la lecture si vous vous reconnaissez dans ma description.
La procédure
Donc vous avez élu de continuer, très bien !
Donc vous avez effectué la "poussée de taquets" sur toutes les tables de données susceptibles de grossir (par opposition aux tables de référence), super !
Alors on fait l’hypothèse qu’un schéma à jour est disponible quelque part, à savoir :
- dans le gestionnaire de versions (il devrait s’y trouver !)
- sur un serveur de test où une version récente a été qualifiée
- au pire sur un serveur de développement.
On peut schématiser les machines en présence dans situation accidentelle
par un triangle dont les trois sommets sont trois entités A, R et P.
- A est le sommet serveur "Accidenté".
- R est le sommet serveur de Restauration, une machine mise à disposition pour l’exercice de restauration
- P est le nouveau serveur de failover de Production, sain, en service bien sûr, mais ne disposant comme données live que d’un backup antérieur, par exemple le backup“du petit matin”, donc avant l’ouverture du business.
- Pendant que mysqld est up, on injecte le schéma recouvré.
- Cela créé les fichiers structurels de base.
- On shutdown le daemon mysqld.
- On copie les fichiers binaires et les *.frm sur le serveur R.
- On démarre mysqld en tant que root.
- Faites un mysqdump de cette base de R, et filez avec ce dump bénit.
- Editez ce dump pour y ajouter des SET FOREIGN_KEY_CHECKS=0 en tête et des SET FOREIGN_KEY_CHECKS=1 en queue de chaque dump, voire ajoutez-y le mots-clef IGNORE après les INSERT.
- Sur le serveur P, logguez-vous sur mysql en lignhe de commande, pour y injecter la base ainsi recouvrée.
On élit une machine tierce de restauration avec un niveau de MySQL très proche.
Préparer les ingrédients minimaux de la réussite, à savoir ce que j’appelle la base orthogonale, c’est à dire le minimum nécessaire et suffisant pour extraire les données :les fichiers ibdata1, ib_logfile0, ib_logfile1 prélevés sur le serveur malade ou défunt, l’ensemble des fichiers *.frm, ainsi qu’un schéma bien connu, au format SQL, ce qui permettra de recréer les jeu de contraintes, vous savez, celles-là mêmes qui sont créées par les commandes
ALTER TABLE <tablename> ADD CONSTRAINT qui viennent habituellement en fin de dump.
On s’y loggue, et on recherche le mot de passe de l’utilisateur en tant duquel le serveur mysqld tourne. C’est un mdp aléatoire créé par l’installation de la Debian.
Voyez /etc/mysql/debian.cnf de la machine A, et notez le mot de passe qui s’y trouve pour un usage ultérieur.
Sur R :
GRANT ALL PRIVILEGES
TO <mysql_user>
ON <databasename>.* TO <username>.*
IDENTIFIED BY "<le mot de passe de debian.cnf>";
FLUSH PRIVILEGES;
Le fait que le dump injecté soit issue d’un serveur sain : le serveur de recouvrement R, nous rassure, et nous permet de complémenter le serveur de failover avec des données live jusqu’à l’heure du crash !