Потеря базы данных превращает рабочий проект в мёртвый код за секунды. Отказ диска, случайное 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:
- Оригинал: работающая база на продакшн-сервере
- Локальный бэкап: ежедневные дампы на том же сервере (/backups/mysql)
- Удалённый бэкап: выгрузка в 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 восстановления:
- Остановить приложение
- Создать снимок текущего состояния БД (на случай ошибки)
- Проверить контрольную сумму бэкапа
- Восстановить дамп в тестовую базу
- Проверить данные
- Переключить приложение на тестовую базу
- Если OK — переименовать базы (test → production)
- Запустить приложение
- Мониторить ошибки
Часто задаваемые вопросы
Как часто нужно делать бэкапы 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.