MySQL : sauvegarde
Un backup MySQL qui n’a jamais été restauré n’est pas un backup, c’est un espoir. L’article précédent parlait du mémo , celui-ci parle de la sauvegarde automatisée et du test de restauration.
Le script de sauvegarde
#!/bin/bash
BACKUP_DIR="/backup/mysql"
DATE=$(date +%Y%m%d)
MYSQL_USER="backup_user"
MYSQL_PASS="password"
mkdir -p "$BACKUP_DIR/$DATE"
# Lister les bases
DATABASES=$(mysql -u $MYSQL_USER -p$MYSQL_PASS -e "SHOW DATABASES" | grep -v Database | grep -v information_schema | grep -v test)
# Dump de chaque base
for db in $DATABASES; do
mysqldump -u $MYSQL_USER -p$MYSQL_PASS \
--add-drop-table \
--single-transaction \
"$db" > "$BACKUP_DIR/$DATE/$db.sql"
gzip "$BACKUP_DIR/$DATE/$db.sql"
done
# Nettoyer les backups de plus de 7 jours
find "$BACKUP_DIR" -type d -mtime +7 -exec rm -rf {} \;
Le --single-transaction est crucial: il démarre une transaction InnoDB et dump dans un snapshot cohérent sans verrouiller les tables. Sans ce flag, le mysqldump verrouille chaque table et votre application en prod attend.
Le cron
0 2 * * * /usr/local/bin/mysql-backup.sh > /dev/null 2>&1
2h du matin, tous les jours. Le > /dev/null évite que cron envoie un mail à chaque run réussi (cron n’envoie que si stdout/stderr n’est pas vide).
Le test de restauration
Le test que personne ne fait et qui se fait en 30 secondes:
# Créer une base temporaire
mysql -u root -p -e "CREATE DATABASE test_restore"
# Restaurer le dump
gunzip -c /backup/mysql/$(date +%Y%m%d)/mon_schema.sql.gz | mysql -u root -p test_restore
# Vérifier les tables
mysql -u root -p -e "SHOW TABLES" test_restore
# Compter les lignes d'une table critique
mysql -u root -p -e "SELECT COUNT(*) FROM users" test_restore
# Supprimer la base de test
mysql -u root -p -e "DROP DATABASE test_restore"
Si le COUNT(*) retourne un nombre proche de ce que l’application voit en prod, le backup est bon. Si le gunzip échoue, le backup est corrompu et vous venez de le découvrir AVANT d’en avoir besoin.
La restauration que personne ne teste
Le script ci-dessus fait trois choses que beaucoup de scripts de backup oublient: --single-transaction (pas de lock), le nettoyage des vieux backups (sinon le disque se remplit) et le gzip (50 Mo de dump devient 8 Mo de gzip). La quatrième chose, le test de restauration, n’est pas dans le script: elle est dans le calendrier, une fois par mois, manuellement.
[edit: 2026] mysqldump est toujours là, mais les serveurs modernes ont des snapshots (LVM, ZFS, les snapshots cloud) qui sont plus rapides que le dump. Le principe du test de restauration n’a pas bougé: un backup non testé n’est pas un backup.
[edit: 2026] L’implémentation Ansible de tout ceci: le rôle mariadb .