LVM snapshot + rsync: ночной бэкап MySQL без окна обслуживания
Автоматизировали бэкап MySQL через LVM snapshot: база замирает на секунды для создания снэпшота, дамп снимается с «заморозки». Окно обслуживания - ноль.
LVM2 snapshots становятся де-факто стандартом для консистентного бэкапа Linux-систем без остановки сервисов
У одного из клиентов MySQL-база росла и к весне перевалила за 30 ГБ. Ночью мы делали честный mysqldump, который на живой базе держал таблицы залоченными минут двадцать. Клиент работает в нескольких часовых поясах, ночи как таковой у базы нет - кто-то всегда пишет. Двадцать минут блокировки ночью - это жалобы утром. Начали искать способ убрать окно.
Что такое LVM snapshot и при чём тут бэкап
LVM2 - это менеджер логических томов, который умеет делать снэпшоты. Снэпшот - это моментальная «фотография» тома в момент создания. Технически работает через copy-on-write: при создании снэпшота ничего не копируется, просто фиксируется точка. Когда исходный том начинает меняться, LVM перед перезаписью блока сначала копирует старое содержимое в снэпшот-том. В итоге снэпшот всегда отражает состояние данных ровно на момент своего создания, как бы ни менялся оригинал.
Для базы данных это означает следующее: если зафиксировать снэпшот в момент, когда база находится в консистентном состоянии, можно работать со снэпшотом сколько угодно долго - база при этом живёт и принимает запросы. Дамп снимается не с живой базы, а с «заморозки».
Единственная тонкость - MySQL перед созданием снэпшота нужно привести в консистентное состояние. Просто снэпшот файловой системы с работающей базой, которая активно пишет в буферы, даст мусор. Решение простое: FLUSH TABLES WITH READ LOCK - база сбрасывает все буферы на диск и блокирует запись. После этого создаём снэпшот и немедленно снимаем блокировку.
Сколько длится блокировка
Вот тут самое интересное для клиента: блокировка держится ровно столько, сколько нужно ядру LVM создать снэпшот. Не время дампа, не время передачи по сети - только создание снэпшота. На нашем железе это занимает около двух секунд вне зависимости от размера базы, потому что физически никакие данные в этот момент не копируются - только метаданные.
После снятия блокировки MySQL продолжает принимать запросы в обычном режиме. Дамп в это время снимается с примонтированного снэпшота сколько угодно долго.
Как это выглядит в скрипте
Схема примерно такая:
mysql -e "FLUSH TABLES WITH READ LOCK"- блокируем запись, сбрасываем буферы на дискlvcreate -L10G -s -n mysql_snap /dev/vg0/mysql- создаём снэпшот нужного размераmysql -e "UNLOCK TABLES"- немедленно снимаем блокировку, база работает- Монтируем снэпшот в отдельную директорию
- Запускаем
mysqldumpилиrsyncс файлами базы - уже без какого-либо влияния на продакшн - Отмонтируем и удаляем снэпшот
Важный момент с размером снэпшота: снэпшот-том нужен не под весь объём базы, а под объём изменений за время дампа. Если за два часа база меняется на 2-3 ГБ, снэпшот на 10 ГБ с запасом. Если снэпшот переполнится до завершения дампа - он станет невалидным, и дамп окажется мусором. Мы установили мониторинг заполненности через Zabbix и пока видим, что 10 ГБ за ночь расходуется в среднем процентов на тридцать.
Ещё одна тонкость - снэпшот нужно монтировать с опцией nouuid если файловая система XFS, иначе монтирование упадёт на конфликте UUID с оригинальным томом. С ext3, которая у нас на этом клиенте, таких проблем нет.
Что получилось на практике
Скрипт крутится на cron каждую ночь. Двадцатиминутная блокировка превратилась в двухсекундную паузу, которую никто не замечает. Дамп снимается около двух часов - за это время снэпшот расходует примерно треть выделенного объёма.
Дамп идёт на локальный диск, после чего rsync переносит его на удалённый бэкап-сервер. Итого имеем: консистентный ежедневный дамп, ненулевая нагрузка от rsync ночью есть, но блокировки базы нет совсем.
Для клиентских задач, которые мы ведём в рамках сопровождения, такая схема закрывает большинство случаев: сервер на Linux, база на LVM-томе, задача не останавливать сервис на время бэкапа. Единственное ограничение - LVM должен быть настроен заранее, переводить уже работающую систему с разметкой без LVM на LVM без переустановки некомфортно.
Что с консистентностью InnoDB
FLUSH TABLES WITH READ LOCK корректно работает с MyISAM - принудительно сбрасывает буферы и держит блокировку. С InnoDB картина сложнее: InnoDB имеет собственный механизм транзакций и redo log. FLUSH TABLES WITH READ LOCK гарантирует, что файлы на диске консистентны с точки зрения MySQL, но внутренний redo log InnoDB на момент снэпшота может содержать незакоммиченные транзакции.
На практике это означает, что при поднятии базы из такого снэпшота InnoDB пройдёт через обычный crash recovery - ровно как после некорректного выключения. Это штатный механизм, он работает корректно. Но знать об этом нужно, чтобы не удивляться, когда тестовое восстановление показывает в логах фразу про recovery.
Тестировать восстановление мы начали с первой же недели - поднимали базу из дампа на тестовой машине и проверяли целостность данных. Пока всё штатно.
- Data Domain на бэкап-таргете: объём хранения упал в 12 раз · 19 марта 2009
- Резервные копии и миф о RAID · 8 ноября 2001