Zálohování MySQL a MariaDB: mysqldump, cron a obnova
Spolehlivé zálohy MySQL/MariaDB bez zamykání tabulek: mysqldump s --single-transaction, automatizace cronem, rotace, šifrování a test obnovy.
Záloha databáze je jedna z věcí, které všichni „mají“, dokud je nepotřebují obnovit. Tento návod ukazuje, jak MySQL nebo MariaDB zálohovat tak, aby záloha byla konzistentní, neblokovala web, automaticky se rotovala a – hlavně – šla obnovit.
Logická vs. fyzická záloha
Logická (mysqldump, mariadb-dump) |
Fyzická (mariabackup, Percona XtraBackup) |
|
|---|---|---|
| Výstup | SQL příkazy | kopie datových souborů |
| Vhodné pro | do desítek GB | velké databáze |
| Obnova | pomalejší (znovu provádí SQL) | rychlá |
| Přenositelnost | mezi verzemi i servery | stejná hlavní verze |
Pro běžný web nebo aplikaci je mysqldump naprosto dostačující. Na MariaDB se od verze 10.5 nástroj jmenuje mariadb-dump; mysqldump zatím funguje jako alias.
Konzistentní dump bez zamykání
mysqldump \
--single-transaction \
--quick \
--routines --triggers --events \
--default-character-set=utf8mb4 \
-u backup -p myapp > myapp.sql
Co jednotlivé přepínače dělají:
--single-transaction– dump proběhne v jedné transakci, takže data jsou konzistentní k jednomu okamžiku a tabulky InnoDB se nezamykají. Pro tabulky MyISAM to nefunguje.--quick– čte řádky postupně místo načtení celé tabulky do paměti.--routines --triggers --events– zahrne uložené procedury, triggery a plánované události. Triggery jsou ve výchozím stavu zapnuté, ostatní ne.- Pro všechny databáze najednou použijte
--all-databases.
Uživatel jen pro zálohy
Nepoužívejte pro zálohy root. Vytvořte uživatele s minimálními právy:
CREATE USER 'backup'@'localhost' IDENTIFIED BY 'dlouhe-nahodne-heslo';
GRANT SELECT, SHOW VIEW, TRIGGER, LOCK TABLES, EVENT, PROCESS ON *.* TO 'backup'@'localhost';
FLUSH PRIVILEGES;
Od MySQL 8.0.21 potřebuje mysqldump právo PROCESS kvůli exportu tablespaces; pokud ho dávat nechcete, přidejte do příkazu --no-tablespaces.
Heslo nepište do příkazu (bylo by vidět v ps a historii shellu). Uložte ho do ~/.my.cnf uživatele, pod kterým zálohy běží:
[client]
user=backup
password=dlouhe-nahodne-heslo
chmod 600 ~/.my.cnf
Skript se kompresí a rotací
/usr/local/bin/db-backup.sh:
#!/usr/bin/env bash
set -euo pipefail
BACKUP_DIR=/var/backups/mysql
KEEP_DAYS=14
DATE=$(date +%F_%H-%M)
mkdir -p "$BACKUP_DIR"
for DB in $(mysql -N -e "SHOW DATABASES" | grep -Ev '^(information_schema|performance_schema|mysql|sys)$'); do
mysqldump --single-transaction --quick --routines --triggers --events "$DB" \
| gzip -c > "$BACKUP_DIR/${DB}_${DATE}.sql.gz"
done
find "$BACKUP_DIR" -name '*.sql.gz' -mtime +"$KEEP_DAYS" -delete
Díky set -o pipefail skript selže, i když spadne mysqldump uprostřed roury – jinak by gzip vrátil úspěch a vy byste měli poškozenou zálohu, o které nevíte.
sudo chmod 700 /usr/local/bin/db-backup.sh
Automatizace cronem
sudo crontab -e
30 2 * * * /usr/local/bin/db-backup.sh >> /var/log/db-backup.log 2>&1
Záloha poběží každý den ve 2:30. Při spuštění jako root se použije /root/.my.cnf.
Záloha mimo server
Záloha na stejném disku vás neochrání před výpadkem disku, smazáním serveru ani ransomwarem. Kopírujte ji jinam, ideálně šifrovaně. Například pomocí rclone na S3 kompatibilní úložiště:
rclone copy /var/backups/mysql remote:moje-zalohy/mysql --max-age 24h
Nebo ji před odesláním zašifrujte:
gpg --symmetric --cipher-algo AES256 myapp_2026-10-12_02-30.sql.gz
Klíč (heslo) uchovávejte mimo zálohovaný server.
Obnova
gunzip < myapp_2026-10-12_02-30.sql.gz | mysql myapp
Databáze myapp musí existovat (CREATE DATABASE myapp CHARACTER SET utf8mb4;). Pokud jste dumpovali s --databases nebo --all-databases, soubor obsahuje i CREATE DATABASE a USE.
Obnova jedné tabulky z velkého dumpu je nepříjemná; jednodušší je obnovit celý dump do dočasné databáze a tabulku z ní zkopírovat:
INSERT INTO myapp.customers SELECT * FROM restore_tmp.customers WHERE id = 42;
Testujte obnovu
Jednou za čas (klidně automaticky) obnovte poslední zálohu na testovací server nebo do Docker kontejneru a ověřte počty řádků:
docker run -d --name restore-test -e MARIADB_ROOT_PASSWORD=test mariadb:11
docker exec -i restore-test mariadb -uroot -ptest -e "CREATE DATABASE myapp"
gunzip < myapp_latest.sql.gz | docker exec -i restore-test mariadb -uroot -ptest myapp
docker exec restore-test mariadb -uroot -ptest -e "SELECT COUNT(*) FROM myapp.orders"
Počkejte pár sekund po startu kontejneru, než bude databáze připravená.
Kontrolní seznam
- [ ]
--single-transactiona InnoDB tabulky - [ ] dedikovaný uživatel, heslo v
~/.my.cnfs právy 600 - [ ]
set -o pipefailve skriptu - [ ] rotace starých záloh
- [ ] kopie mimo server, šifrovaná
- [ ] pravidelný test obnovy
Pro body v čase (obnova „na 14:37“) je potřeba ještě binární log (log_bin) a mysqlbinlog – to už je téma pro samostatný článek.