Как сделать бэкап MySQL: полное руководство по mysqldump и автоматизации

Потеря базы данных превращает рабочий проект в мёртвый код за секунды. Отказ диска, случайное DROP TABLE, взлом с удалением данных, повреждение файловой системы — сценариев масса. Статистика показывает что около 60% компаний, потерявших критичные данные без возможности восстановления, закрываются в течение года. Бэкапы — не опция, а необходимость.

mysqldump входит в стандартную поставку MySQL и MariaDB. Это консольная утилита, работающая на любой операционной системе где установлен MySQL. Не требует дополнительного софта, конфигурации или лицензий. Открываете терминал, вводите команду — получаете дамп базы. Простота делает mysqldump основным инструментом резервного копирования для проектов любого размера.

Содержание:

Как работает mysqldump изнутри

mysqldump подключается к MySQL-серверу как обычный клиент. Никакого прямого доступа к файлам данных не требуется. Утилита выполняет SELECT-запросы ко всем таблицам базы, получает данные, преобразует их в SQL-команды CREATE TABLE и INSERT. Результат записывается в текстовый файл.

Логический дамп содержит SQL-инструкции полностью воссоздающие базу. Открываете файл текстовым редактором — видите читаемый SQL-код. Это принципиально отличается от физических бэкапов (копий файлов данных), которые нельзя прочитать без MySQL.

Преимущество логических дампов — универсальность. Дамп созданный на MySQL 5.7 загружается в MySQL 8.0 без проблем. Можете мигрировать между разными серверами, операционными системами, даже между MySQL и MariaDB. Формат SQL универсален.

Процесс дампа работает так: mysqldump открывает соединение, получает список таблиц через SHOW TABLES, для каждой таблицы выполняет SHOW CREATE TABLE (структура) и SELECT * (данные), преобразует результаты в INSERT-команды, пишет в выходной поток. Для базы с сотнями таблиц и миллионами строк это занимает время.

Скорость дампа зависит от размера базы и производительности диска. База 1 ГБ дампится за минуту на SSD, 10 ГБ за 10-15 минут, 100 ГБ может занять несколько часов. Сетевая задержка при дампе удалённого сервера добавляет времени.

Базовый синтаксис: первый дамп базы данных

Простейшая команда бэкапа выглядит так:

mysqldump -u username -p database_name > backup.sql

Разберём каждый элемент. Флаг -u username указывает пользователя MySQL. Флаг -p означает что mysqldump запросит пароль интерактивно. database_name — имя базы которую дампите. Символ > перенаправляет вывод в файл backup.sql.

После ввода команды система попросит пароль. Вводите его вслепую (символы не отображаются) и нажимаете Enter. Начнётся процесс дампа. Прогресс не отображается, но можете отслеживать рост размера файла командой ls -lh backup.sql в другом терминале.

Пример реального использования:

mysqldump -u root -p wordpress_db > /backups/wordpress_2026-01-14.sql

Эта команда создаёт дамп базы wordpress_db в директории /backups с датой в имени файла. Дата в имени критична для идентификации бэкапов. Файл без даты через неделю превращается в загадку — какие данные там, насколько они свежие?

Проблема паролей в командной строке

Вариант с паролем прямо в команде технически работает:

mysqldump -u root -pSecretPassword123 database_name > backup.sql

Обратите внимание — между -p и паролем нет пробела. Это синтаксис MySQL. Но такой подход опасен. Команда попадает в историю bash (~/.bash_history), логи системы, вывод ps aux пока команда выполняется. Любой пользователь сервера может подсмотреть пароль.

Безопасное решение — конфигурационный файл .my.cnf в домашней директории пользователя:

nano ~/.my.cnf

Содержимое файла:

[mysqldump]
user=root
password=SecretPassword123

Устанавливаете права доступа только для владельца:

chmod 600 ~/.my.cnf

Теперь команда упрощается и становится безопаснее:

mysqldump database_name > backup.sql

mysqldump автоматически читает учётные данные из .my.cnf. Пароль не светится нигде. Для продакшн-серверов это стандартная практика.

Дамп всех баз данных одной командой

Флаг --all-databases дампит все базы на сервере:

mysqldump --all-databases > all_databases.sql

Результирующий файл содержит команды CREATE DATABASE и USE для каждой базы, затем все таблицы. Удобно для полного бэкапа сервера перед мажорным обновлением MySQL или миграцией на другое железо.

Размер файла будет значительным. Сервер с десятком баз по 1 ГБ каждая даст дамп на 10+ ГБ. Сжатие обязательно:

mysqldump --all-databases | gzip > all_databases.sql.gz

Вертикальная черта (pipe) | передаёт вывод mysqldump напрямую в gzip. Сжатие происходит на лету, промежуточный несжатый файл не создаётся. Экономия места и времени.

Дамп нескольких выбранных баз

Опция --databases позволяет указать несколько баз через пробел:

mysqldump --databases wordpress joomla phpbb > multiple_sites.sql

Эта команда дампит три базы в один файл. Удобно для серверов где на одном MySQL крутится несколько сайтов. Восстановление тоже простое — один файл содержит всё.

Разница между --databases и дампом без флага: с флагом дамп включает команды CREATE DATABASE и USE. Без флага эти команды отсутствуют, при восстановлении нужно вручную создать базу или указать в какую базу импортировать.

Пример дампа без --databases:

mysqldump wordpress_db > wordpress.sql

При восстановлении нужно сначала создать базу:

mysql -u root -p -e "CREATE DATABASE wordpress_db"
mysql -u root -p wordpress_db < wordpress.sql

С флагом --databases база создастся автоматически:

mysqldump --databases wordpress_db > wordpress.sql
mysql -u root -p < wordpress.sql

Какой вариант использовать? Для автоматизированных скриптов лучше --databases — меньше шагов при восстановлении. Для ручного бэкапа одной базы можно без флага.

Критичные опции mysqldump для продакшна

--single-transaction: дамп без блокировок

Опция --single-transaction критична для InnoDB-таблиц. Она создаёт консистентный снимок базы без блокировки таблиц для записи. Механизм работает через транзакции — mysqldump открывает транзакцию с уровнем изоляции REPEATABLE READ, дампит данные, закрывает транзакцию.

mysqldump --single-transaction database_name > backup.sql

Во время дампа пользователи продолжают работать с базой: вставляют данные, обновляют записи, удаляют строки. Дамп видит состояние базы на момент начала транзакции и не замечает последующих изменений. Это обеспечивает консистентность — все таблицы дампятся из одного временного среза.

Без --single-transaction mysqldump блокирует таблицы командами LOCK TABLES. База становится только для чтения на время дампа. Для небольших баз это секунды, для больших — минуты или часы. Production-сервис с таким простоем неприемлем.

Важное ограничение: --single-transaction работает только с InnoDB. Для MyISAM-таблиц транзакции не поддерживаются, блокировки неизбежны. Если база содержит mix таблиц (InnoDB и MyISAM), полностью избежать блокировок не получится.

Проверить типы таблиц можно запросом:

SELECT TABLE_NAME, ENGINE 
FROM information_schema.TABLES 
WHERE TABLE_SCHEMA = 'database_name';

Современные проекты используют InnoDB по умолчанию. MyISAM устарел и используется редко.

--quick: экономия памяти при дампе

Опция --quick заставляет mysqldump читать таблицы построчно вместо загрузки полной таблицы в память перед дампом.

mysqldump --quick --single-transaction database_name > backup.sql

По умолчанию (без --quick) mysqldump загружает результат SELECT в буфер, затем пишет в файл. Для таблицы на 10 ГБ это требует 10+ ГБ RAM. Сервер с 8 ГБ RAM упадёт с ошибкой нехватки памяти.

С --quick mysqldump читает по одной строке, сразу пишет в файл, освобождает память. Потребление RAM константное независимо от размера таблицы. Для больших баз --quick обязателен.

Недостаток --quick — если дамп прерван (ctrl+c, ошибка сети, закончилось место на диске), таблица остаётся залоченной до завершения транзакции. Без --quick прерванный дамп сразу освобождает ресурсы.

Для большинства случаев плюсы --quick перевешивают минусы. Современные версии mysqldump включают --quick по умолчанию, но явное указание не помешает.

--routines и --triggers: не забудьте процедуры

По умолчанию mysqldump дампит только таблицы и данные. Хранимые процедуры, функции и триггеры игнорируются. Для проектов использующих этот функционал нужны дополнительные флаги.

Опция --routines добавляет в дамп stored procedures и functions:

mysqldump --routines --single-transaction database_name > backup.sql

Без этого флага после восстановления базы приложение сломается с ошибками "procedure not found". Отловить такую проблему сложно — дамп и восстановление проходят без ошибок, но функциональность отсутствует.

Триггеры включаются по умолчанию. Но если используете флаг --skip-triggers (случайно или намеренно), триггеры не попадут в дамп. Проверяйте скрипты бэкапа что этот флаг отсутствует.

Полная команда бэкапа с процедурами и триггерами:

mysqldump \
  --single-transaction \
  --routines \
  --triggers \
  --quick \
  database_name | gzip > backup.sql.gz

Бэкслеш \ позволяет разбить длинную команду на несколько строк для читабельности.

--events: планировщик задач MySQL

MySQL Events — планировщик задач внутри базы данных (аналог cron для MySQL). Если база использует события, добавьте флаг --events:

mysqldump --events --rout
mysqldump --events --routines --single-transaction database_name > backup.sql

Без --events планировщик задач не восстановится. Автоматические очистки старых данных, периодические агрегации, scheduled отчёты перестанут работать.

Проверить наличие событий можно запросом:

SELECT EVENT_NAME, EVENT_DEFINITION 
FROM information_schema.EVENTS 
WHERE EVENT_SCHEMA = 'database_name';
SELECT EVENT_NAME, EVENT_DEFINITION 
FROM information_schema.EVENTS 
WHERE EVENT_SCHEMA = 'database_name';

Если запрос возвращает строки — события есть, флаг --events обязателен.

--opt: группа оптимизаций

Флаг --opt включает набор опций оптимизирующих дамп и восстановление. Включён по умолчанию, но стоит знать что внутри:

  • --add-drop-table: добавляет DROP TABLE IF EXISTS перед CREATE TABLE
  • --add-locks: обрамляет INSERT в LOCK TABLES / UNLOCK TABLES
  • --create-options: включает специфичные для MySQL опции CREATE TABLE
  • --disable-keys: отключает индексы перед INSERT, включает после
  • --extended-insert: группирует INSERT в большие multi-value запросы
  • --lock-tables: блокирует таблицы для консистентности
  • --quick: читает строки последовательно
  • --set-charset: сохраняет кодировку

Для InnoDB-баз флаг --single-transaction отменяет --lock-tables из --opt, что позволяет дампить без блокировок.

Если хотите отключить --opt (редко нужно):

mysqldump --skip-opt database_name > backup.sql

Тогда придётся вручную указывать нужные опции.

--compress: сжатие трафика при дампе удалённого сервера

Опция --compress сжимает данные передаваемые между mysqldump и MySQL-сервером:

mysqldump --compress -h remote-server.com database_name > backup.sql

Это полезно при дампе удалённого сервера через медленное интернет-соединение. Сжатие снижает объём передаваемых данных, ускоряет процесс.

Не путайте --compress с gzip для выходного файла. --compress сжимает сетевой трафик между клиентом и сервером. gzip сжимает результирующий файл. Можно использовать оба:

mysqldump --compress -h remote-server.com database_name | gzip > backup.sql.gz

Для локальных дампов (mysqldump и MySQL на одном сервере) --compress бесполезен — нет сетевого трафика.

--master-dаta: информация для репликации

Опция --master-data добавляет в дамп позицию binlog для настройки репликации:

mysqldump --master-data=2 --single-transaction database_name > backup.sql

Значение 2 записывает позицию binlog как комментарий. Значение 1 записывает как SQL-команду CHANGE MASTER TO. В большинстве случаев используйте 2 — позиция видна, но не применяется автоматически.

Эта опция нужна при настройке реплика-реплики из дампа. Без позиции binlog slave не знает с какого момента начинать репликацию.

Для простых бэкапов (не для репликации) флаг не нужен.

Восстановление базы данных из дампа

Базовое восстановление

Импорт дампа выполняется клиентом mysql:

mysql -u root -p database_name < backup.sql

Команда читает SQL-инструкции из файла и выполняет их последовательно. Если база data_name не существует, создайте её заранее:

mysql -u root -p -e "CREATE DATABASE database_name"
mysql -u root -p database_name < backup.sql

Для дампа созданного с флагом --databases имя базы не требуется — дамп содержит команду CREATE DATABASE:

mysql -u root -p < backup.sql

MySQL автоматически создаст базу и импортирует данные.

Восстановление из сжатого дампа

Распаковка и импорт одной командой:

gunzip < backup.sql.gz | mysql -u root -p database_name

Или через zcat (делает то же самое):

zcat backup.sql.gz | mysql -u root -p database_name

Данные распаковываются потоком и сразу импортируются. Промежуточный несжатый файл не создаётся, экономится место на диске.

Мониторинг процесса восстановления

Импорт больших дампов идёт долго и не показывает прогресс. Для мониторинга используйте pv (pipe viewer):

apt install pv  # установка на Debian/Ubuntu
pv backup.sql | mysql -u root -p database_name

pv отображает скорость передачи данных, объём переданного, процент завершения (если размер файла известен), ETA. Вывод выглядит так:

1.2GiB 0:05:32 [3.8MiB/s] [=========>          ] 45% ETA 0:06:42

Для сжатых файлов:

pv backup.sql.gz | gunzip | mysql -u root -p database_name

Восстановление только определённых таблиц

Иногда нужно восстановить не всю базу, а конкретные таблицы. Для этого извлекаете нужные части из дампа.

Поиск определённой таблицы в дампе:

grep -n "CREATE TABLE \`table_name\`" backup.sql

Команда покажет номер строки где начинается определение таблицы. Затем извлекаете эту секцию:

sed -n '12345,23456p' backup.sql | mysql -u root -p database_name

Где 12345 — строка начала, 23456 — строка конца определения таблицы и данных.

Проще использовать mysqldump с флагом --tables для бэкапа конкретных таблиц изначально:

mysqldump database_name table1 table2 > specific_tables.sql
mysql -u root -p database_name < specific_tables.sql

Восстановление на другой сервер

Дамп с одного сервера легко импортируется на другой. Типичный сценарий — миграция с dev на продакшн или настройка нового репликаа.

Создаёте дамп на source-сервере:

mysqldump --single-transaction --routines --triggers production_db | gzip > production.sql.gz

Копируете файл на destination-сервер:

scp production.sql.gz user@new-server:/tmp/

Импортируете на новом сервере:

ssh user@new-server
gunzip < /tmp/production.sql.gz | mysql -u root -p production_db

Убедитесь что версия MySQL на destination не старше чем на source. MySQL 8.0 дамп не загружается в MySQL 5.7. Обратная совместимость обычно работает.

Устранение ошибок при восстановлении

Ошибка "Table already exists"

Если база не пустая, импорт свалится на CREATE TABLE. Решение — дропнуть базу и создать заново:

mysql -u root -p -e "DROP DATABASE database_name"
mysql -u root -p -e "CREATE DATABASE database_name"
mysql -u root -p database_name < backup.sql

Или используйте флаг --force который игнорирует ошибки и продолжает импорт:

mysql -u root -p --force database_name < backup.sql

Этот флаг опасен — можете пропустить критичные ошибки. Проверяйте логи после импорта.

Ошибка "Incorrect string value" или проблемы кодировки

Несоответствие кодировок между дампом и базой. Убедитесь что база создана с правильной кодировкой:

mysql -u root -p -e "CREATE DATABASE database_name CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"
mysql -u root -p database_name < backup.sql

utf8mb4 поддерживает все Unicode-символы включая emoji. Стандартный utf8 в MySQL ограничен.

Ошибка "MySQL server has gone away"

Происходит при импорте больших INSERT-запросов если параметр max_allowed_packet слишком мал. Увеличьте его временно:

mysql -u root -p --max_allowed_packet=512M database_name < backup.sql

Или измените в конфиге /etc/mysql/my.cnf:

[mysqld]
max_allowed_packet=512M

После изменения перезапустите MySQL:

systemctl restart mysql

Проверка успешности восстановления

После импорта проверьте что все таблицы на месте:

mysql -u root -p database_name -e "SHOW TABLES"

Сравните количество таблиц с оригинальной базой. Проверьте количество записей в ключевых таблицах:

mysql -u root -p database_name -e "SELECT COUNT(*) FROM users"

Запустите приложение и убедитесь что оно работает корректно. Автоматические тесты после восстановления — лучшая практика для критичных систем.

Автоматизация бэкапов через cron

Ручные бэкапы никто не делает систематически. Через неделю забудете, через месяц база останется без защиты. Автоматизация критична.

Создание скрипта бэкапа

Создаём bash-скрипт /usr/local/bin/mysql_backup.sh:

#!/bin/bash

# Конфигурация
DB_USER="root"
DB_NAME="production_db"
BACKUP_DIR="/backups/mysql"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/${DB_NAME}_${DATE}.sql.gz"
LOG_FILE="/var/log/mysql_backup.log"
RETENTION_DAYS=7

# Создаём директорию если не существует
mkdir -p "$BACKUP_DIR"

# Логирование начала
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Starting backup of $DB_NAME" >> "$LOG_FILE"

# Выполняем бэкап
if mysqldump \
    --single-transaction \
    --routines \
    --triggers \
    --events \
    --quick \
    "$DB_NAME" | gzip > "$BACKUP_FILE"; then

    # Успех
    FILESIZE=$(ls -lh "$BACKUP_FILE" | awk '{print $5}')
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] Backup completed successfully: $BACKUP_FILE ($FILESIZE)" >> "$LOG_FILE"

    # Удаляем старые бэкапы
    find "$BACKUP_DIR" -name "${DB_NAME}_*.sql.gz" -mtime +$RETENTION_DAYS -delete
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] Cleaned up backups older than $RETENTION_DAYS days" >> "$LOG_FILE"

else
    # Ошибка
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: Backup failed!" >> "$LOG_FILE"
    exit 1
fi

Делаем скрипт исполняемым:

chmod +x /usr/local/bin/mysql_backup.sh

Учётные данные читаются из ~/.my.cnf (настроили выше), поэтому пароль не нужен в скрипте.

Расширенный скрипт с уведомлениями

Версия с email-уведомлениями об ошибках:

#!/bin/bash

# Конфигурация
DB_USER="root"
DB_NAME="production_db"
BACKUP_DIR="/backups/mysql"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/${DB_NAME}_${DATE}.sql.gz"
LOG_FILE="/var/log/mysql_backup.log"
RETENTION_DAYS=7
ALERT_EMAIL="[email protected]"
MIN_BACKUP_SIZE=1048576  # 1 МБ в байтах

# Функция отправки email
send_alert() {
    local subject="$1"
    local message="$2"
    echo "$message" | mail -s "$subject" "$ALERT_EMAIL"
}

# Создаём директорию
mkdir -p "$BACKUP_DIR"

# Логирование начала
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Starting backup of $DB_NAME" >> "$LOG_FILE"

# Выполняем бэкап
if mysqldump \
    --single-transaction \
    --routines \
    --triggers \
    --events \
    --quick \
    "$DB_NAME" | gzip > "$BACKUP_FILE"; then

    # Проверяем размер файла
    FILESIZE=$(stat -c%s "$BACKUP_FILE")
    FILESIZE_HUMAN=$(ls -lh "$BACKUP_FILE" | awk '{print $5}')

    if [ "$FILESIZE" -lt "$MIN_BACKUP_SIZE" ]; then
        # Подозрительно маленький файл
        ERROR_MSG="Backup file is suspiciously small: $FILESIZE_HUMAN"
        echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: $ERROR_MSG" >> "$LOG_FILE"
        send_alert "MySQL Backup Warning" "$ERROR_MSG\nFile: $BACKUP_FILE"
        exit 1
    fi

    # Успех
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] Backup completed: $BACKUP_FILE ($FILESIZE_HUMAN)" >> "$LOG_FILE"

    # Удаляем старые бэкапы
    DELETED=$(find "$BACKUP_DIR" -name "${DB_NAME}_*.sql.gz" -mtime +$RETENTION_DAYS -delete -print | wc -l)
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] Removed $DELETED old backups" >> "$LOG_FILE"

else
    # Ошибка выполнения mysqldump
    ERROR_MSG="mysqldump command failed with exit code $?"
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: $ERROR_MSG" >> "$LOG_FILE"
    send_alert "MySQL Backup Failed" "$ERROR_MSG\nCheck $LOG_FILE for details"
    exit 1
fi

Для работы email установите mailutils:

apt install mailutils

Настройка cron

Редактируем crontab:

crontab -e

Добавляем задачу для ежедневного бэкапа в 2:00 ночи:

0 2 * * * /usr/local/bin/mysql_backup.sh

Формат cron: минута час день месяц день_недели команда.

Примеры расписаний:

# Каждую ночь в 3:30
30 3 * * * /usr/local/bin/mysql_backup.sh

# Каждые 6 часов
0 */6 * * * /usr/local/bin/mysql_backup.sh

# Каждый понедельник в 1:00
0 1 * * 1 /usr/local/bin/mysql_backup.sh

# Дважды в день: в 02:00 и 14:00
0 2,14 * * * /usr/local/bin/mysql_backup.sh

Проверка что задача добавлена:

crontab -l

Логирование и мониторинг

Cron по умолчанию отправляет вывод команд на email пользователя (если настроен). Для явного указания email добавьте в начало crontab:

MAILTO=admin@example.com
0 2 * * * /usr/local/bin/mysql_backup.sh

Просмотр логов бэкапов:

tail -f /var/log/mysql_backup.log

Для ротации логов создайте конфиг /etc/logrotate.d/mysql_backup:

/var/log/mysql_backup.log {
    daily
    rotate 30
    compress
    delaycompress
    missingok
    notifempty
}

logrotate автоматически сжимает и удаляет старые логи.

Бэкап нескольких баз

Скрипт для бэкапа всех баз отдельными файлами:

#!/bin/bash

BACKUP_DIR="/backups/mysql"
DATE=$(date +%Y%m%d_%H%M%S)
LOG_FILE="/var/log/mysql_backup.log"

mkdir -p "$BACKUP_DIR"

# Получаем список баз (исключаем системные)
DATABASES=$(mysql -e "SHOW DATABASES" | grep -Ev "(Database|information_schema|performance_schema|mysql|sys)")

for DB in $DATABASES; do
    BACKUP_FILE="$BACKUP_DIR/${DB}_${DATE}.sql.gz"
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] Backing up $DB..." >> "$LOG_FILE"

    if mysqldump \
        --single-transaction \
        --routines \
        --triggers \
        --events \
        "$DB" | gzip > "$BACKUP_FILE"; then

        FILESIZE=$(ls -lh "$BACKUP_FILE" | awk '{print $5}')
        echo "[$(date '+%Y-%m-%d %H:%M:%S')] ✓ $DB completed ($FILESIZE)" >> "$LOG_FILE"
    else
        echo "[$(date '+%Y-%m-%d %H:%M:%S')] ✗ $DB FAILED!" >> "$LOG_FILE"
    fi
done

# Очистка старых бэкапов
find "$BACKUP_DIR" -name "*.sql.gz" -mtime +7 -delete
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Cleanup completed" >> "$LOG_FILE"

Параллельные бэкапы для ускорения

Если баз много и они большие, бэкап последовательно занимает часы. Параллельный запуск ускоряет процесс:

#!/bin/bash

BACKUP_DIR="/backups/mysql"
DATE=$(date +%Y%m%d_%H%M%S)
MAX_PARALLEL=4

mkdir -p "$BACKUP_DIR"

DATABASES=$(mysql -e "SHOW DATABASES" | grep -Ev "(Database|information_schema|performance_schema|mysql|sys)")

backup_database() {
    local DB=$1
    local BACKUP_FILE="$BACKUP_DIR/${DB}_${DATE}.sql.gz"
    mysqldump --single-transaction --routines --triggers "$DB" | gzip > "$BACKUP_FILE"
}

export -f backup_database
export BACKUP_DIR DATE

# GNU Parallel или xargs для параллельного выполнения
echo "$DATABASES" | xargs -n 1 -P $MAX_PARALLEL -I {} bash -c 'backup_database "$@"' _ {}

Требуется установка GNU Parallel:

apt install parallel

Параллельность 4 означает что одновременно дампятся 4 базы. Подбирайте значение под ресурсы сервера — слишком много параллельных дампов перегрузит диск и CPU.

Ротация старых бэкапов: экономия места без потери защиты

Хранить все бэкапы навечно невозможно — место закончится быстро. База 10 ГБ даёт дамп 2 ГБ. За год накопится 730 ГБ (дневные бэкапы). Ротация удаляет старые файлы по правилам, баланс между защитой данных и стоимостью хранения.

Простая ротация: последние N дней

Самая простая схема — храним последние 7 дней:

find /backups/mysql -name "*.sql.gz" -mtime +7 -delete

Опция -mtime +7 находит файлы старше 7 дней. -delete удаляет их. Команду добавляйте в конец скрипта бэкапа.

Проблема простой ротации — теряете историю. Если нужно откатиться на две недели назад, а хранятся только семь дней, данные потеряны безвозвратно.

Grandfather-Father-Son ротация

GFS-стратегия хранит бэкапы трёх уровней:

  • Daily (Son): последние 7 дневных бэкапов
  • Weekly (Father): последние 4 недельных бэкапа
  • Monthly (Grandfather): последние 12 месячных бэкапов

Итого храните 23 бэкапа (7+4+12) вместо 365, экономя 94% места при сохранении годовой истории.

Скрипт реализации GFS:

#!/bin/bash

DB_NAME="production_db"
BACKUP_BASE="/backups/mysql"
DAILY_DIR="$BACKUP_BASE/daily"
WEEKLY_DIR="$BACKUP_BASE/weekly"
MONTHLY_DIR="$BACKUP_BASE/monthly"
DATE=$(date +%Y%m%d_%H%M%S)
DAY_OF_WEEK=$(date +%u)  # 1-7, понедельник-воскресенье
DAY_OF_MONTH=$(date +%d)

# Создаём директории
mkdir -p "$DAILY_DIR" "$WEEKLY_DIR" "$MONTHLY_DIR"

# Создаём дневной бэкап
DAILY_BACKUP="$DAILY_DIR/${DB_NAME}_${DATE}.sql.gz"
mysqldump --single-transaction --routines "$DB_NAME" | gzip > "$DAILY_BACKUP"

# Копируем в weekly если воскресенье (день 7)
if [ "$DAY_OF_WEEK" -eq 7 ]; then
    cp "$DAILY_BACKUP" "$WEEKLY_DIR/"
fi

# Копируем в monthly если первое число месяца
if [ "$DAY_OF_MONTH" -eq 01 ]; then
    cp "$DAILY_BACKUP" "$MONTHLY_DIR/"
fi

# Удаляем старые дневные бэкапы (старше 7 дней)
find "$DAILY_DIR" -name "*.sql.gz" -mtime +7 -delete

# Удаляем старые недельные (старше 28 дней, 4 недели)
find "$WEEKLY_DIR" -name "*.sql.gz" -mtime +28 -delete

# Удаляем старые месячные (старше 365 дней, 12 месяцев)
find "$MONTHLY_DIR" -name "*.sql.gz" -mtime +365 -delete

echo "Backup completed: $DAILY_BACKUP"
echo "Daily backups: $(ls -1 $DAILY_DIR | wc -l)"
echo "Weekly backups: $(ls -1 $WEEKLY_DIR | wc -l)"
echo "Monthly backups: $(ls -1 $MONTHLY_DIR | wc -l)"

Такая схема даёт:

  • Откат на любой день последней недели
  • Откат на начало любой из последних 4 недель
  • Откат на начало любого месяца последнего года

Расширенная GFS с годовыми бэкапами

Для долгосрочного архива добавьте Yearly уровень:

# После создания DAILY_BACKUP добавьте:

YEARLY_DIR="$BACKUP_BASE/yearly"
mkdir -p "$YEARLY_DIR"

# Копируем в yearly если 1 января
if [ "$(date +%m%d)" = "0101" ]; then
    cp "$DAILY_BACKUP" "$YEARLY_DIR/"
fi

# Удаляем годовые бэкапы старше 5 лет
find "$YEARLY_DIR" -name "*.sql.gz" -mtime +1825 -delete

Теперь храните 5 лет истории с минимальным использованием места.

Калькулятор использования места

Подсчитайте сколько места займут бэкапы при GFS:

Размер дампа: 2 ГБ
Daily (7): 7 × 2 = 14 ГБ
Weekly (4): 4 × 2 = 8 ГБ
Monthly (12): 12 × 2 = 24 ГБ
Yearly (5): 5 × 2 = 10 ГБ
Итого: 56 ГБ

Против 730 ГБ при хранении дневных бэкапов за год — экономия огромная.

Мониторинг доступного места

Добавьте в скрипт проверку свободного места перед бэкапом:

# Проверяем свободное место (минимум 10 ГБ)
AVAILABLE_SPACE=$(df /backups | tail -1 | awk '{print $4}')
REQUIRED_SPACE=10485760  # 10 ГБ в КБ

if [ "$AVAILABLE_SPACE" -lt "$REQUIRED_SPACE" ]; then
    echo "ERROR: Not enough disk space for backup!"
    echo "Available: $(($AVAILABLE_SPACE / 1024 / 1024)) GB"
    send_alert "Backup Failed" "Insufficient disk space on /backups"
    exit 1
fi

Предотвращает ситуацию когда дамп начинается, место заканчивается, дамп неполный и бесполезный.

Выгрузка бэкапов на удалённое хранилище

Локальные бэкапы защищают от ошибок пользователей и повреждения данных, но бесполезны при отказе железа. Диск сгорел — бэкапы на нём сгорели тоже. Удалённое хранилище обязательно.

Amazon S3 и S3-совместимые хранилища

S3 дёшево для холодного хранения, надёжно (99.999999999% durability), доступно из любой точки мира. Подходит не только Amazon S3, но и совместимые сервисы: DigitalOcean Spaces, Wasabi, Backblaze B2, MinIO.

Установка AWS CLI:

apt install awscli

Настройка credentials:

aws configure

Вводите Ключ доступа ID, Секретный ключ доступа, регион, формат вывода. Учётные данные сохраняются в ~/.aws/credentials.

Выгрузка бэкапа после создания:

#!/bin/bash

DB_NAME="production_db"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="/tmp/${DB_NAME}_${DATE}.sql.gz"
S3_BUCKET="s3://my-backups-bucket/mysql/"

# Создаём бэкап
mysqldump --single-transaction --routines "$DB_NAME" | gzip > "$BACKUP_FILE"

# Загружаем в S3
aws s3 cp "$BACKUP_FILE" "$S3_BUCKET"

# Удаляем локальную копию
rm "$BACKUP_FILE"

# Удаляем старые бэкапы из S3 (старше 30 дней)
aws s3 ls "$S3_BUCKET" | while read -r line; do
    FILE_DATE=$(echo $line | awk '{print $1" "$2}')
    FILE_NAME=$(echo $line | awk '{print $4}')
    FILE_EPOCH=$(date -d "$FILE_DATE" +%s)
    CURRENT_EPOCH=$(date +%s)
    DAYS_OLD=$(( ($CURRENT_EPOCH - $FILE_EPOCH) / 86400 ))

    if [ $DAYS_OLD -gt 30 ]; then
        aws s3 rm "${S3_BUCKET}${FILE_NAME}"
        echo "Deleted old backup: $FILE_NAME (${DAYS_OLD} days old)"
    fi
done

Для S3-совместимых сервисов (не Amazon) укажите endpoint:

aws s3 cp "$BACKUP_FILE" "$S3_BUCKET" --endpoint-url=https://nyc3.digitaloceanspaces.com

S3 Glacier для архивного хранения дешевле, но восстановление занимает часы. Для регулярных бэкапов используйте S3 Standard.

Автоматическое шифрование для S3

Шифрование бэкапов перед отправкой в облако защищает от утечек:

#!/bin/bash

DB_NAME="production_db"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="/tmp/${DB_NAME}_${DATE}.sql.gz"
ENCRYPTED_FILE="${BACKUP_FILE}.gpg"
S3_BUCKET="s3://my-backups-bucket/mysql/"
GPG_PASSPHRASE="your-strong-passphrase"

# Создаём бэкап
mysqldump --single-transaction --routines "$DB_NAME" | gzip > "$BACKUP_FILE"

# Шифруем
echo "$GPG_PASSPHRASE" | gpg --batch --yes --passphrase-fd 0 -c "$BACKUP_FILE"

# Загружаем в S3
aws s3 cp "$ENCRYPTED_FILE" "$S3_BUCKET"

# Удаляем локальные копии
rm "$BACKUP_FILE" "$ENCRYPTED_FILE"

Расшифровка при восстановлении:

aws s3 cp s3://my-backups-bucket/mysql/production_db_20260114.sql.gz.gpg .
gpg -d production_db_20260114.sql.gz.gpg | gunzip | mysql -u root -p production_db

GPG запросит passphrase интерактивно.

FTP/SFTP выгрузка

Если есть второй сервер или FTP-хранилище, используйте SFTP (безопаснее чем FTP):

#!/bin/bash

DB_NAME="production_db"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="/tmp/${DB_NAME}_${DATE}.sql.gz"
REMOTE_HOST="backup-server.example.com"
REMOTE_USER="backupuser"
REMOTE_DIR="/backups/mysql"

# Создаём бэкап
mysqldump --single-transaction --routines "$DB_NAME" | gzip > "$BACKUP_FILE"

# Выгружаем через SFTP
scp "$BACKUP_FILE" "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_DIR}/"

# Удаляем локальную копию
rm "$BACKUP_FILE"

Для автоматизации без ввода пароля настройте SSH-ключи:

# На локальном сервере
ssh-keygen -t rsa -b 4096 -f ~/.ssh/backup_key -N ""

# Копируем публичный ключ на удалённый сервер
ssh-copy-id -i ~/.ssh/backup_key.pub [email protected]

# Используем ключ в scp
scp -i ~/.ssh/backup_key "$BACKUP_FILE" "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_DIR}/"

rsync для инкрементных выгрузок

rsync передаёт только изменения, экономя трафик и время:

#!/bin/bash

BACKUP_DIR="/backups/mysql"
REMOTE_HOST="backup-server.example.com"
REMOTE_USER="backupuser"
REMOTE_DIR="/backups/mysql"

# rsync синхронизирует локальную и удалённую директории
rsync -avz --delete \
    -e "ssh -i ~/.ssh/backup_key" \
    "$BACKUP_DIR/" \
    "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_DIR}/"

Опции:

  • -a: архивный режим (сохраняет права, время модификации)
  • -v: verbose (показывает процесс)
  • -z: сжатие при передаче
  • --delete: удаляет файлы на удалённой стороне если их нет локально

rsync идемпотентен — повторный запуск передаёт только новые файлы.

rclone для облачных хранилищ

rclone поддерживает десятки облачных провайдеров: Google Drive, Dropbox, OneDrive, Backblaze, Wasabi и другие.

Установка:

curl https://rclone.org/install.sh | sudo bash

Настройка (интерактивный wizard):

rclone config

Пример выгрузки в Google Drive:

#!/bin/bash

DB_NAME="production_db"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="/tmp/${DB_NAME}_${DATE}.sql.gz"
RCLONE_REMOTE="gdrive:backups/mysql"

# Создаём бэкап
mysqldump --single-transaction --routines "$DB_NAME" | gzip > "$BACKUP_FILE"

# Загружаем через rclone
rclone copy "$BACKUP_FILE" "$RCLONE_REMOTE"

# Удаляем локальную копию
rm "$BACKUP_FILE"

# Удаляем старые бэкапы (старше 30 дней)
rclone delete "$RCLONE_REMOTE" --min-age 30d

rclone показывает прогресс, возобновляет прерванные загрузки, работает с ограниченной пропускной способностью (полезно чтобы не забить канал).

Стратегия 3-2-1 бэкапов

Правило 3-2-1 — индустриальный стандарт:

  • 3 копии данных (оригинал + 2 бэкапа)
  • 2 разных типа носителей (например, локальный диск + облако)
  • 1 копия за пределами площадки (за пределами офиса/дата-центра)

Реализация для MySQL:

  1. Оригинал: работающая база на продакшн-сервере
  2. Локальный бэкап: ежедневные дампы на том же сервере (/backups/mysql)
  3. Удалённый бэкап: выгрузка в S3 или другое облако

Локальный бэкап даёт быстрое восстановление (секунды). Удалённый защищает от аварии (пожар, кража сервера, полный отказ).

Мониторинг успешности выгрузки

Скрипт должен проверять что файл действительно загружен:

#!/bin/bash

BACKUP_FILE="/tmp/backup.sql.gz"
S3_BUCKET="s3://my-backups-bucket/mysql/"

# Загружаем
aws s3 cp "$BACKUP_FILE" "$S3_BUCKET"

# Проверяем что файл существует в S3
if aws s3 ls "${S3_BUCKET}$(basename "$BACKUP_FILE")" > /dev/null 2>&1; then
    echo "Upload successful"
    rm "$BACKUP_FILE"
else
    echo "ERROR: Upload failed!"
    exit 1
fi

Для критичных систем проверяйте размер файла в облаке — он должен совпадать с локальным.

Проверка целостности: бэкапы которые не восстанавливаются бесполезны

Худшее что может случиться — обнаружить после аварии что все бэкапы повреждены или неполные. Регулярная проверка бэкапов обязательна. Статистика показывает что около 34% компаний обнаруживают невосстанов имые бэкапы только в момент критической необходимости.

Автоматическая проверка после создания

Простейшая проверка — убедиться что файл создан и имеет разумный размер:

#!/bin/bash

DB_NAME="production_db"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="/backups/mysql/${DB_NAME}_${DATE}.sql.gz"
MIN_SIZE=1048576  # 1 МБ
EXPECTED_SIZE=2147483648  # 2 ГБ примерно

# Создаём дамп
mysqldump --single-transaction --routines "$DB_NAME" | gzip > "$BACKUP_FILE"

# Проверяем что файл существует
if [ ! -f "$BACKUP_FILE" ]; then
    echo "ERROR: Backup file not created!"
    exit 1
fi

# Проверяем размер
ACTUAL_SIZE=$(stat -c%s "$BACKUP_FILE")

if [ "$ACTUAL_SIZE" -lt "$MIN_SIZE" ]; then
    echo "ERROR: Backup file too small ($ACTUAL_SIZE bytes)"
    rm "$BACKUP_FILE"  # Удаляем битый файл
    exit 1
fi

# Предупреждение если размер сильно отличается от ожидаемого
SIZE_DIFF=$(($ACTUAL_SIZE - $EXPECTED_SIZE))
SIZE_DIFF=${SIZE_DIFF#-}  # Абсолютное значение
THRESHOLD=$(($EXPECTED_SIZE / 5))  # 20% отклонение

if [ "$SIZE_DIFF" -gt "$THRESHOLD" ]; then
    echo "WARNING: Backup size differs significantly from expected"
    echo "Expected: $(($EXPECTED_SIZE / 1024 / 1024)) MB"
    echo "Actual: $(($ACTUAL_SIZE / 1024 / 1024)) MB"
fi

echo "Backup verification passed: $BACKUP_FILE"

Проверка валидности сжатого файла

gzip-архив может быть повреждён при передаче или записи. Проверка целостности:

# Проверяем что gzip-файл не повреждён
if ! gunzip -t "$BACKUP_FILE" 2>/dev/null; then
    echo "ERROR: Backup file is corrupted (gzip test failed)"
    exit 1
fi

Опция -t тестирует архив без распаковки. Быстро работает даже для больших файлов.

Тестовое восстановление в отдельную базу

Самая надёжная проверка — реально восстановить дамп в тестовую базу:

#!/bin/bash

DB_NAME="production_db"
TEST_DB="test_restore_$(date +%s)"
BACKUP_FILE="/backups/mysql/latest_backup.sql.gz"

# Создаём тестовую базу
mysql -e "CREATE DATABASE $TEST_DB"

# Восстанавливаем дамп
if gunzip < "$BACKUP_FILE" | mysql "$TEST_DB" 2>&1 | tee /tmp/restore_test.log; then

    # Проверяем количество таблиц
    TABLES_COUNT=$(mysql -N -e "SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA='$TEST_DB'")
    EXPECTED_TABLES=42  # Укажите реальное количество

    if [ "$TABLES_COUNT" -eq "$EXPECTED_TABLES" ]; then
        echo "✓ Restore test passed: $TABLES_COUNT tables restored"
    else
        echo "✗ ERROR: Expected $EXPECTED_TABLES tables, got $TABLES_COUNT"
        mysql -e "DROP DATABASE $TEST_DB"
        exit 1
    fi

    # Проверяем ключевые таблицы
    KEY_TABLES="users orders products"
    for TABLE in $KEY_TABLES; do
        COUNT=$(mysql -N -e "SELECT COUNT(*) FROM $TEST_DB.$TABLE" 2>/dev/null || echo "0")
        if [ "$COUNT" -eq "0" ]; then
            echo "✗ WARNING: Table $TABLE is empty or missing"
        else
            echo "✓ Table $TABLE: $COUNT rows"
        fi
    done

    # Удаляем тестовую базу
    mysql -e "DROP DATABASE $TEST_DB"
    echo "Test database cleaned up"

else
    echo "✗ ERROR: Restore failed! Check /tmp/restore_test.log"
    mysql -e "DROP DATABASE IF EXISTS $TEST_DB"
    exit 1
fi

Запускайте такой тест еженедельно через cron. Для больших баз восстановление занимает время, поэтому тестируйте ночью когда нагрузка низкая.

Контрольные суммы для детекции повреждений

MD5 или SHA256 хеши защищают от повреждения файлов:

#!/bin/bash

BACKUP_FILE="/backups/mysql/production_db_20260114.sql.gz"
CHECKSUM_FILE="${BACKUP_FILE}.sha256"

# Создаём хеш после дампа
sha256sum "$BACKUP_FILE" > "$CHECKSUM_FILE"

echo "Контрольная сумма created: $CHECKSUM_FILE"

Проверка перед восстановлением:

#!/bin/bash

BACKUP_FILE="/backups/mysql/production_db_20260114.sql.gz"
CHECKSUM_FILE="${BACKUP_FILE}.sha256"

# Проверяем хеш
if sha256sum -c "$CHECKSUM_FILE" --status; then
    echo "✓ Контрольная сумма verified: file is intact"
else
    echo "✗ ERROR: File is corrupted!"
    exit 1
fi

Контрольные суммы особенно важны при передаче файлов по сети. Копируете бэкап с удалённого сервера — проверяйте хеш после копирования.

Мониторинг успешности бэкапов

Настройте внешний мониторинг который проверяет что бэкапы создаются. Например, через healthchecks.io или собственную систему мониторинга:

#!/bin/bash

HEALTHCHECK_URL="https://hc-ping.com/your-unique-id"

# В конце скрипта бэкапа
if [ $? -eq 0 ]; then
    # Уведомляем мониторинг об успехе
    curl -fsS --retry 3 "$HEALTHCHECK_URL" > /dev/null
fi

Если пинг не приходит в течение 25 часов (для дневного бэкапа), мониторинг отправит алерт. Защищает от ситуации когда cron задача сломалась, а никто не заметил.

Практические советы и лучшие практики

Тестируйте восстановление систематически

График: первая неделя месяца — восстанавливаете случайный недельный бэкап, вторая неделя — месячный, третья — дневной. К концу года протестируете десятки бэкапов.

Automation:

#!/bin/bash
# monthly_restore_test.sh

# Выбираем случайный месячный бэкап
BACKUP=$(ls /backups/mysql/monthly/*.sql.gz | shuf -n 1)

# Создаём временную базу и тестируем
TEST_DB="restore_test_$(date +%s)"
mysql -e "CREATE DATABASE $TEST_DB"
gunzip < "$BACKUP" | mysql "$TEST_DB"

# Проверяем структуру
TABLES=$(mysql -N -e "SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA='$TEST_DB'")
echo "Restored $TABLES tables from $BACKUP"

# Cleanup
mysql -e "DROP DATABASE $TEST_DB"

Запускайте через cron первого числа каждого месяца.

Мониторьте тренды размера бэкапов

Внезапное изменение размера — признак проблем:

#!/bin/bash

CURRENT_BACKUP="/backups/mysql/latest.sql.gz"
PREV_BACKUPS="/backups/mysql/*.sql.gz"

CURRENT_SIZE=$(stat -c%s "$CURRENT_BACKUP")
AVG_SIZE=$(stat -c%s $PREV_BACKUPS | awk '{sum+=$1} END {print sum/NR}')

# Отклонение больше 30% — предупреждение
DEVIATION=$(echo "scale=2; ($CURRENT_SIZE - $AVG_SIZE) / $AVG_SIZE * 100" | bc)

if [ ${DEVIATION%.*} -gt 30 ] || [ ${DEVIATION%.*} -lt -30 ]; then
    echo "WARNING: Backup size deviation: ${DEVIATION}%"
    echo "Current: $(($CURRENT_SIZE / 1024 / 1024)) MB"
    echo "Average: $(($AVG_SIZE / 1024 / 1024)) MB"
fi

Резкое увеличение может означать утечку данных (логи пишутся в БД), резкое уменьшение — неполный дамп.

Разделяйте большие базы

Таблицы логов часто раздувают базу. Дампите их отдельно с более короткой срок хранения:

# Основные данные: храним год
mysqldump --single-transaction database_name \
    --ignore-table=database_name.logs \
    --ignore-table=database_name.analytics | gzip > main_data.sql.gz

# Логи: храним неделю
mysqldump --single-transaction database_name logs analytics | gzip > logs.sql.gz

Экономит место и ускоряет восстановление production-данных.

Используйте отдельного пользователя для бэкапов

Создайте MySQL-пользователя с минимальными правами:

CREATE USER 'backup'@'localhost' IDENTIFIED BY 'strong_password';
GRANT SELECT, SHOW VIEW, RELOAD, REPLICATION CLIENT, EVENT, TRIGGER ON *.* TO 'backup'@'localhost';
FLUSH PRIVILEGES;

Если учётные данные backup-пользователя скомпрометированы, злоумышленник не сможет менять данные.

Шифруйте чувствительные бэкапы

Для медицинских, финансовых, персональных данных шифрование обязательно:

# Бэкап с немедленным шифрованием
mysqldump --single-transaction database_name | gzip | \
    gpg --symmetric --cipher-algo AES256 --output backup.sql.gz.gpg

# Восстановление
gpg --decrypt backup.sql.gz.gpg | gunzip | mysql database_name

Храните passphrase в password manager, не в скриптах.

Документируйте всё

Readme с каждым бэкапом, runbook для восстановления процесса, контакты на случай аварии. Во время кризиса времени разбираться нет.

Checklist восстановления:

  1. Остановить приложение
  2. Создать снимок текущего состояния БД (на случай ошибки)
  3. Проверить контрольную сумму бэкапа
  4. Восстановить дамп в тестовую базу
  5. Проверить данные
  6. Переключить приложение на тестовую базу
  7. Если OK — переименовать базы (test → production)
  8. Запустить приложение
  9. Мониторить ошибки

Часто задаваемые вопросы

Как часто нужно делать бэкапы MySQL?

Минимум — ежедневно ночью. Для критичных данных — каждые 4-6 часов. Для финансовых систем — непрерывное резервное копирование через репликацию плюс дневные дампы. Частота зависит от RPO (Recovery Point Objective — цель точки восстановления) — сколько данных можете потерять.

Безопасно ли хранить пароль MySQL в скрипте cron?

Нет. Используйте ~/.my.cnf с правами 600 или переменную окружения MYSQL_PWD. Создайте отдельного пользователя MySQL только для бэкапов с правами SELECT, RELOAD, REPLICATION CLIENT — не root.

Можно ли делать дамп без остановки MySQL?

Да. Для InnoDB используйте --single-transaction, дамп будет консистентным без блокировок. Для MyISAM-таблиц нужны блокировки (--lock-tables), база будет только для чтения во время дампа.

Сколько места нужно для хранения бэкапов?

Сжатый дамп обычно 10-20% от размера базы. База 10 ГБ → дамп 1-2 ГБ. С GFS-ротацией (7 daily + 4 weekly + 12 monthly) для базы 10 ГБ нужно ~50 ГБ. Добавьте 30% запас для роста данных.

Что делать если mysqldump падает с ошибкой нехватки памяти?

Используйте --quick (читает строки последовательно без загрузки в память). Дампите базу по частям через --tables. Для баз 100+ ГБ переходите на Percona XtraBackup — он работает с файлами напрямую, не через SQL.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *