Comment sauvegarder un million de bases de données chaque jour ?

Temps de lecture estimé : 12 minute(s)

Au sein de ses offres d’Hébergement Web, OVH inclut une ou plusieurs bases de données SQL mutualisées et propose de recourir à des bases de données privatives optionnelles, les « SQL Privé ». Ces deux types de bases de données sont sauvegardés quotidiennement par OVH. Cela représente actuellement un peu plus d’un million de bases. Une volumétrie importante, dont découlent des problématiques intéressantes : comment répartir les backups dans le temps pour éviter de créer un pic de charge sur l’infrastructure ? Quelle méthodologie utiliser pour réaliser les sauvegardes ? Explications avec l’équipe chargée de cette mission chez OVH.

Quand réalisons-nous les backups ?

Réaliser des sauvegardes, qu’il s’agisse de bases de données ou de tout autre type de service, produit naturellement une montée en charge sur l’infrastructure. En termes d’Input/Output (I/O) bien sûr, car les données doivent être copiées, et au niveau du réseau, car les données doivent être transférées d’une machine à une autre.

Cette problématique est bien connue des sysadmins, et c’est la raison pour laquelle les backups sont très souvent effectués pendant les heures creuses du service concerné, en général la nuit.

Nous avons historiquement adopté cette approche assez intuitive. Pour une majorité écrasante des utilisateurs des hébergements web OVH, le creux de trafic est situé entre 01:00 et 07:00 du matin, heure française. Les sauvegardes des bases de données étaient donc logiquement planifiées durant ce créneau horaire.

Pourquoi donc ne procédons-nous plus comme cela aujourd’hui ? Cette approche a vite montré ses limites. Durant le créneau 01:00-07:00, nos indicateurs système viraient souvent au rouge vif. On constatait des goulots d’étranglement, les dumps (exports de base de données SQL) mettaient plus de temps à être exécutés et la qualité de service était menacée, car le créneau imparti aux sauvegardes était parfois dépassé. Cela signifiait que la plateforme devait alors absorber la charge liée à la réalisation des backups tout en encaissant l’affluence croissante, au petit matin, de visiteurs sur les sites hébergés… Bref, ce n’était plus acceptable.

Nous avons alors adopté une approche différente et réparti la réalisation des sauvegardes tout au long de la journée, ceci pour conserver des performances optimales sur les hébergements web à n’importe quelle heure du jour et de la nuit.

L’algorithme de répartition

Pour réaliser cette répartition uniforme des sauvegardes durant la journée, nous avons eu besoin d’associer une heure de la journée à une base de données.

Nous ne pouvions pas effectuer cette répartition (ou «sharding») uniquement en fonction du nom de la base. En effet, sur la globalité de l’infrastructure, un nombre incommensurable de bases s’intitulent « wordpress », « prestashop », ou même « test » ou « demo ». Nous nous serions retrouvés à lancer un backup simultané de toutes les bases « wordpress » au même horaire, ce qui n’aurait pas été très efficace.

Bien qu’il soit unique, nous ne pouvions pas non plus faire un sharding basé uniquement sur le nom de l’instance hébergeant la base de données. Avec beaucoup moins d’instances que de bases, la loi des grands nombres fait que la répartition sur la journée des sauvegardes en fonction de l’identité des instances aurait été moins uniforme qu’une répartition en fonction de l’identité des bases elles-mêmes. Nous avons donc combiné ces deux informations, et nous utilisons aujourd’hui la concaténation du nom de la base et de l’instance pour effectuer notre sharding.

Il ne nous restait plus qu’à trouver une formule permettant d’associer à cette chaîne de caractères une heure de la journée, c’est-à-dire un nombre compris entre 1 et 1 440 (le nombre de minutes dans une journée). Le nombre 1 440 étant divisible par 16 (merci aux Babyloniens d’avoir opté pour la très divisible base 60 pour le calcul des minutes et des secondes), nous avons utilisé une simple fonction de hachage cryptographique (digest) hexadécimale (le sha512) pour générer le fameux nombre :

int(hashlib.sha512(instance_name + "." + db_name).hexdigest()[:90], 16) % 1440 L'algorithme de répartition

Comme nous le montre le graphique suivant, les sauvegardes sont réparties uniformément dans le temps.

Comment réalisons-nous les backups ?

Il est possible d’effectuer les backups des bases de données suivant deux méthodes. Soit on exporte les données au format SQL, soit on sauvegarde les bases dans un format lisible par le système de gestion de base de données (le SGBD ; MySQL par exemple). Chaque méthode a ses avantages, ses inconvénients, et ses cas d’utilisation typiques. Nous avons opté pour une combinaison des deux méthodes, ceci afin de cumuler les avantages de chacune.

Les dumps

L’idée derrière la méthode du dump est de générer un fichier texte de commandes SQL qui, lorsque ces dernières sont rejouées, recrée ex nihilo la base de données dans l’état où elle se trouvait au moment de l’export. Elle a pour avantage de permettre la migration de données vers d’autres versions du SGBD, ou d’autres moteurs. Il est également possible de modifier le fichier à la main avant de le réimporter (ce qui peut s’avérer utile dans le cas où l’on souhaite cloner un environnement de production pour réaliser des tests, mais que certaines tables volumineuses sont inutiles par exemple).

Au vu de ces avantages, nous utilisons cette méthode et nous vous donnons également accès aux dumps, visibles et téléchargeables directement depuis votre espace client. Comme évoqué un peu plus haut, les dumps sont effectués tous les jours à heure fixe, et sont conservés pendant un mois glissant.

Pour qu’un dump soit cohérent (c’est-à-dire qu’il reflète parfaitement l’état de la base à un instant donné), on peut utiliser deux méthodes :

Verrouiller les tables

On maintient la base dans un état, et on place en attente les connexions, qui s’empilent. Une fois que le dump est terminé, on déverrouille et on dépile la file d’attente.

Avantages : la méthode est compatible avec tous les moteurs (dont les principaux, MyISAM et InnoDB), et le dump est toujours cohérent.

Inconvénient : les tables sont verrouillées, et il faut attendre la fin du dump pour rendre la base de données (et donc le site web) de nouveau accessible. Ce n’est pas gênant dans une large majorité des cas, mais cela peut l’être lorsque le dump est long et les connexions nombreuses. Le plafond du nombre maximal de connexions simultanées à la base peut alors être atteint. Notons qu’augmenter ce plafond ne résoudrait pas le problème, mais ne ferait que le reporter un peu plus loin. De plus, si nous limitons le nombre de connexions, c’est pour protéger votre serveur : une connexion requiert de la RAM, et au-delà de 200 connexions simultanées (le maximum autorisé pour des instances SQL Privé), la RAM allouée aux connexions en cours pourrait conduire votre instance en Out Of Memory.

Utiliser une transaction

Avantages : le dump est totalement transparent. Aucun verrouillage. Le dump est également toujours cohérent.

Inconvénients : ce n’est possible qu’avec un moteur gérant les transactions (InnoDB). C’est-à-dire que s’il y a ne serait-ce qu’une seule table en MyISAM, le dump serait incohérent. De plus, même avec uniquement de l’InnoDB, si un ALTER, CREATE, DROP, RENAME ou TRUNCATE TABLE est effectué pendant le dump, alors il sera incohérent.

Au vu de tout cela, nous avons combiné les deux méthodes : si votre base de données est 100 % InnoDB, alors nous utilisons une transaction. Sinon, nous utilisons le verrouillage des tables.

C’est pourquoi nous vous conseillons vivement de passer toutes vos tables au format InnoDB, qui est inclus et activé dans toutes les distributions fournies par MySQL AB depuis la version 4 et est devenu le moteur par défaut à partir de MySQL 5.5.5 (voir le guide) !

Les sauvegardes au format du SGBD

Effectuer un dump d’une base de données comporte, nous l’avons vu, de nombreux avantages en comparaison avec une sauvegarde « classique ». La méthode présente pourtant un inconvénient : la durée de restauration, qui dépend de la taille du dump.

La volumétrie de dumps que nous réalisons quotidiennement nous a permis d’établir des statistiques précises sur la durée des exports de bases et des restaurations. Vous trouverez ci-dessous le résultat des régressions linéaires que nous avons calculées sur le temps de réponse de plus de 40 000 exports de bases 100 % InnoDB (un choix qui permet de ne pas tenir compte du temps d’attente avant l’obtention du verrouillage de la base de données).

$ ./lr.py list y=0.142865163057*x1+4.65205077853 R2=0.792955 Temps de dump en fonction de la taille de la base, en Mo

R2 est ici le coefficient de corrélation. Une valeur à 0.8 signifie que la corrélation est forte, c’est-à-dire que déterminer le temps d’un dump en fonction de la taille de la base a du sens, que les deux valeurs sont étroitement liées.

Nous nous sommes demandé si d’autres paramètres entraient en compte dans la détermination du temps de dump et de restauration. Nous avons donc réitéré l’exercice avec plusieurs variables. Nombre de rows, taille moyenne des enregistrements… Au final, le seul calcul à peine plus pertinent consiste à considérer le nombre d’enregistrements (rows) en plus de la taille de la base :

$ ./lr.py list y=2.62412392084*x1+0.130888461045*x2+4.60953501036 R2=0.800904 Temps de dump en fonction du nombre d'enregistrements (en million) et de la taille de la base (en Mo)

Forts de ces enseignements, nous avons adapté notre stratégie pour minimiser le temps de restauration des bases en cas d’incident sur nos systèmes, car nous savons qu’une base indisponible signifie la plupart du temps que le site web concerné est inaccessible, ce qui est critique pour l’utilisateur. Ainsi, lorsque la taille d’une instance hébergeant des bases de données est supérieure à 4 Go (ce qui correspond à une durée moyenne de restauration de 10 minutes), nous doublons systématiquement les dumps de sauvegardes au format SGBD. Celle-ci a pour avantage une restauration beaucoup plus rapide en cas de problème sur un cluster de stockage. Ces sauvegardes, qui sont réalisées pour l’ensemble des instances mutualisées hébergeant des bases, et au cas par cas pour les instances SQL privé dépassant les 4 Go, ne sont pas mises à disposition des utilisateurs ; elles ne servent qu’en cas de problème sur un cluster de stockage. L’utilisateur, quant à lui, n’a accès qu’à ses dumps.

Pour effectuer ces sauvegardes au format SGBD, nous utilisons, pour MySQL et MariaDB, l’outil XtraBackup en mode full-backup (et non en mode incrémental), afin de faciliter la restauration. XtraBackup est un outil open source de Percona, et permet de faire des sauvegardes cohérentes, sans verrouillage des tables, et ce, quel que soit le moteur utilisé (MyISAM, InnoDB…). Notez que les sauvegardes avec XtraBackup se font à l’échelle d’une instance complète, et non d’une simple base de données. Pour PostgreSQL, nous utilisons l’utilitaire pg_basebackup.

Vérification des tables et besoins en RAM

Avant d’effectuer un dump (qu’il soit quotidien ou à la demande), nous vérifions l’état de vos tables, et les réparons au besoin. Sur les instances MySQL et MariaDB, nous utilisons pour cela la commande mysqlcheck. Il s’agit, notons-le, d’un souci qui tend à disparaître : les dernières versions de MySQL et MariaDB gèrent de mieux en mieux les écritures d’opérations interrompues par des crashes, et le problème n’existe plus du tout avec PostgreSQL.

La vérification d’une table, et surtout sa réparation, peut nécessiter beaucoup plus de RAM que celle dont dispose votre instance. C’est pourquoi, pendant tout le temps du mysqlcheck et du dump, nous augmentons temporairement la RAM de votre instance, en lui ajoutant 4 Go. Si vos bases sont suffisamment volumineuses, vous pouvez constater cet ajout de RAM dans votre espace client :

Dans le cas où les dumps prennent moins d’une minute, cet ajout de RAM peut passer inaperçu entre deux prises de mesures. Vous ne le verrez alors pas dans les graphiques de votre espace client.

Il est à noter que ce pic de mémoire disponible va écraser votre graphique, rendant presque illisible la courbe d’utilisation mémoire. N’hésitez donc pas à cliquer sur « Maximum RAM limit » en bas du graphe pour ne faire apparaître que la courbe d’utilisation mémoire.

Où stockons-nous les backups ?

Un backup n’a d’intérêt que s’il est stocké sur une plateforme tierce. Aussi, nous conservons vos dumps sur la plateforme Public Cloud Storage, qui est totalement décorrélée de nos plateformes de gestion des bases de données du service d’hébergement web. Vos backups sont ainsi répartis sur trois réplicats synchrones, situés au sein de trois domaines d’incident distincts, l’un à Gravelines, l’un à Roubaix, et le dernier à Strasbourg. Ils y seront conservés pendant un mois.

Les backups XtraBackup et pg_basebackup des PrivateSQL de la plateforme de Paris (hébergements web créés avant juillet 2016) sont quant à eux stockés sur un cluster Ceph autre que celui utilisé pour stocker les données de production. Enfin, les backups de la plateforme de Gravelines (hébergements web créés après juillet 2016) sont quant à eux conservés sur les disques durs locaux (et les données de production sur un cluster Ceph).

On recrute dans la team!

Ces problématiques vous intéressent ? On recrute dans la team databases! Rendez-vous sur OVH Careers pour consulter nos offres !