Перенос большой базы с минимальным даунтаймом
Когда база данных весит десятки или сотни гигабайт, привычная схема «остановили сервис — сделали дамп — перенесли — восстановили» превращается в простой на несколько часов, а иногда и на всю ночь. Для тестового стенда это нормально, для продакшена с живыми пользователями — часто неприемлемо. Ниже — методика, которая сводит простой к секундам-минутам за счёт того, что основной объём данных переезжает заранее, пока старая база продолжает работать.
Содержание
Почему наивный дамп-восстановление не подходит для больших баз
Посчитайте на пальцах: pg_dump или mysqldump базы на 200 ГБ на скромном сервере идёт часами — сначала выгрузка, потом передача файла по сети, потом восстановление на новом сервере (создание индексов, применение constraints — это отдельная долгая стадия, которая на дампе не видна, но занимает не меньше времени, чем сама загрузка данных). Всё это время сервис либо полностью недоступен, либо работает на замороженных данных.
Проблема не в инструменте — pg_dump и mysqldump отлично справляются с задачей «перенести данные». Проблема в том, что вы держите сервис остановленным ровно на то время, которое требуется на перенос всего объёма. При 10 ГБ это может быть 10-15 минут — приемлемо. При 300 ГБ это уже часы — и здесь наивный подход перестаёт работать как бизнес-решение, даже если технически он абсолютно корректен.
Ключевая идея переноса с минимальным простоем — разделить «перенос основного объёма данных» и «остановку сервиса» на два независимых события. Первое можно делать сколько угодно долго без всякого простоя. Второе должно занимать секунды.
Стратегия в четыре шага
Вся методика укладывается в четыре шага, и только последний требует реальной остановки записи в базу:
- Первичная полная синхронизация — снимаете консистентный слепок старой базы и разворачиваете его на новом сервере, пока старая база продолжает штатно работать и принимать запросы. Этот шаг может занять часы — это нормально, простоя сервиса он не создаёт.
- Настройка репликации от старой базы к новой — новая база начинает получать все изменения, которые происходят в старой, пока или после первичной синхронизации. Отставание (лаг) репликации постепенно сокращается до нуля или околонулевых значений.
- Короткое окно простоя для переключения — останавливаете запись в старую базу, дожидаетесь, пока последние изменения долетят через репликацию на новую, переключаете приложение на новый адрес.
- Верификация целостности — сверяете количество записей и контрольные суммы по ключевым таблицам между старой и новой базой, прежде чем окончательно отключать старый сервер.
Шаги 1 и 2 могут идти параллельно много часов или даже суток при действительно больших объёмах — это ваш рабочий буфер. Шаги 3 и 4 — то самое узкое окно, ради которого всё затевалось: реально оно занимает от нескольких секунд до нескольких минут, если репликация настроена правильно и лаг перед переключением уже близок к нулю.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверШаг 1: первичная синхронизация без остановки сервиса
Для PostgreSQL логическая репликация требует консистентной точки старта, синхронизированной с бинарным дампом. Практический способ:
-- на старом сервере: создаём слот и получаем имя снапшота
SELECT pg_create_logical_replication_slot('mig_slot', 'pgoutput');
-- вернёт имя слота и связанный snapshot, который нужно передать pg_dump
Дальше снимаете дамп с привязкой к этому снапшоту, чтобы данные и точка старта репликации были согласованы:
pg_dump --snapshot=<имя_снапшота> \
-h old-db-host -U migrator -d mydb \
-Fc -f mydb.dump
# переносите файл на новый сервер и восстанавливаете
pg_restore -h new-db-host -U migrator -d mydb -j 4 mydb.dump
Флаг -j 4 распараллеливает восстановление на несколько ядер — на большой базе это ощутимо сокращает время создания индексов. Для очень больших объёмов логический дамп/восстановление может оказаться медленнее, чем физическая копия — тогда рассмотрите pg_basebackup (потоковая копия файлов кластера) с последующей настройкой физической репликации, если логическая репликация вам не обязательна именно как механизм, а важен сам факт «старый сервер продолжает работать, пока копируются файлы».
Для MySQL на больших объёмах логический mysqldump часто вообще не рассматривают — он слишком медленный и создаёт нагрузку блокировками. Стандартный инструмент здесь — Percona XtraBackup, физический бэкап без остановки сервиса:
xtrabackup --backup --target-dir=/backup/full \
--user=migrator --password=***
xtrabackup --prepare --target-dir=/backup/full
# переносите каталог на новый сервер, затем
xtrabackup --copy-back --target-dir=/backup/full \
--datadir=/var/lib/mysql
chown -R mysql:mysql /var/lib/mysql
Важное преимущество XtraBackup для этой методики: он сохраняет позицию бинарного лога на момент завершения бэкапа в файле xtrabackup_binlog_info — именно от этой позиции вы потом стартуете репликацию, без разрывов и дублей. Подробно про установку и параметры — в статье про Percona XtraBackup на Ubuntu 24.04.
Шаг 2: репликация — PostgreSQL и MySQL
После того как новая база наполнена данными на момент снапшота, запускаете репликацию, чтобы она продолжала получать все изменения из старой базы.
PostgreSQL, логическая репликация. На старом сервере создаёте публикацию (обычно на все таблицы или на нужный список):
CREATE PUBLICATION mig_pub FOR ALL TABLES;
На новом сервере — подписку, указывая, что слот уже существует и начальную копию данных делать не нужно (вы её уже перенесли дампом на шаге 1):
CREATE SUBSCRIPTION mig_sub
CONNECTION 'host=old-db-host dbname=mydb user=repl_user password=***'
PUBLICATION mig_pub
WITH (copy_data = false, create_slot = false, slot_name = 'mig_slot');
Прогресс отслеживаете через pg_stat_replication на старом сервере и pg_stat_subscription на новом. Нюансы, о которых стоит знать заранее: логическая репликация не переносит DDL (изменения структуры таблиц во время миграции нужно будет применять на обеих базах вручную или временно заморозить), не реплицирует последовательности (sequences) автоматически — их текущие значения после переключения нужно синхронизировать отдельным запросом, и требует у таблиц первичного ключа или REPLICA IDENTITY, иначе UPDATE/DELETE не реплицируются. Подробнее о настройке и типовых граблях — в статьях как установить и настроить репликацию PostgreSQL и как работает репликация и отставание реплики.
MySQL, репликация через binlog. На старом сервере должен быть включён бинарный лог в формате ROW (binlog_format=ROW, log_bin=ON, уникальный server-id). Позицию для старта репликации берёте из файла, который оставил XtraBackup:
cat /backup/full/xtrabackup_binlog_info
# mysql-bin.000045 154321
CHANGE REPLICATION SOURCE TO
SOURCE_HOST='old-db-host',
SOURCE_USER='repl_user',
SOURCE_PASSWORD='***',
SOURCE_LOG_FILE='mysql-bin.000045',
SOURCE_LOG_POS=154321;
START REPLICA;
(CHANGE REPLICATION SOURCE TO и START REPLICA — актуальный синтаксис для современных версий MySQL 8; в более старых сборках и в некоторых форках MySQL/MariaDB те же команды называются CHANGE MASTER TO и START SLAVE — проверьте синтаксис своей версии перед тем, как копировать команды один в один.)
Лаг проверяете через SHOW REPLICA STATUS\G — интересует поле Seconds_Behind_Source (в старом синтаксисе Seconds_Behind_Master): пока оно не станет стабильным нулём, к переключению не переходите.
Шаг 3 и 4: окно переключения и проверка целостности
Когда лаг репликации стабильно околонулевой (новая база «догнала» старую и не отстаёт), наступает единственный по-настоящему рискованный момент — переключение:
- Остановите запись в старую базу. Проще всего — на уровне приложения (перевести в режим обслуживания) или на уровне прокси/балансировщика перед базой. Прямая блокировка на СУБД (
ALTER SYSTEM SET default_transaction_read_only = onдля PostgreSQL илиSET GLOBAL read_only = ONдля MySQL) тоже работает, но убедитесь, что уже открытые сессии с активными транзакциями не смогут дописать данные в обход — иногда для этого нужно дополнительно прибить активные соединения приложения. - Дождитесь, пока лаг репликации станет нулевым. Для логической репликации PostgreSQL сверяете LSN на старом и новом сервере (
pg_current_wal_lsn()на источнике и позицию, до которой применена подписка); для MySQL —Seconds_Behind_Source = 0и отсутствие необработанных событий в relay-логе. На корректно настроенной репликации это секунды, максимум пара минут — не часы. - Переключите приложение на новый сервер — смена строки подключения, DNS-записи или конфига прокси (pgbouncer, HAProxy) в зависимости от вашей архитектуры.
- Сверьте целостность данных, прежде чем радоваться и отключать старый сервер. Минимальный набор проверок — количество строк по ключевым таблицам:
-- одинаковый запрос на старой и новой базе
SELECT count(*) FROM orders;
SELECT count(*) FROM users;
SELECT max(id), max(updated_at) FROM orders;
Для более строгой проверки в мире MySQL есть pt-table-checksum из Percona Toolkit — он сравнивает контрольные суммы данных по чанкам между источником и репликой и явно указывает на расхождения, если они есть. В PostgreSQL готового аналога такого уровня нет — обычно ограничиваются сверкой количества строк, MAX(id)/MAX(updated_at) по ключевым таблицам и контрольной выборкой случайных записей построчно. Это не даёт математической гарантии побайтового совпадения, но на практике ловит подавляющее большинство реальных проблем — потерянные строки, не докатившиеся до реплики DDL-изменения, рассинхронизацию sequences.
Только после того как счётчики сошлись и приложение стабильно работает с новой базой, отключайте репликацию и выводите старый сервер из эксплуатации — не раньше.
Инструменты и честная оценка трудозатрат
Кроме описанных выше pg_dump/pg_basebackup и XtraBackup, для миграций с минимальным простоем существуют и более специализированные инструменты — они автоматизируют часть ручной работы (создание слотов, отслеживание позиций, сверку схем), но требуют отдельного изучения, и версии/возможности у них меняются достаточно быстро, чтобы не приводить здесь точные номера — смотрите актуальную документацию конкретного инструмента перед тем как полагаться на него в продакшене. Для PostgreSQL стоит присмотреться к инструментам категории «логическая миграция без даунтайма» поверх встроенной логической репликации; для MySQL — к экосистеме вокруг Percona Toolkit и managed-сервисам облачных провайдеров, если ваша инфраструктура туда вписывается.
Честно: настройка репликации ради одноразовой миграции — это заметно больше первоначальных усилий, чем простой дамп-восстановление на выходных. Нужно разобраться с правами на репликацию, открыть сеть между старым и новым сервером (пусть временно), обработать нюансы вроде sequences и DDL, а на финальном окне действовать по чёткому чек-листу без права на ошибку. Если база весит 5-20 ГБ и наивный простой в 15-30 минут ночью никого не беспокоит — не усложняйте, сделайте обычный дамп-восстановление, это и есть верное разумное решение. Разворачивать репликацию имеет смысл именно тогда, когда объём данных делает наивный простой измеримым в часах и это неприемлемо для бизнеса: интернет-магазин, платёжный сервис, что угодно с активными пользователями в моменте переключения. Общий план подготовки к переезду на новый сервер, включая нетехнические организационные моменты, разобран в статье как составить план миграции на новый сервер, а базовый вариант переноса без такой сложности — в статье миграция базы данных между серверами.
Отдельно стоит продумать, куда вообще переезжаете. Если новая база растёт и упирается в диск или RAM старого VPS, есть смысл сразу переезжать на конфигурацию с запасом — расчёт под большие базы разобран в статье выделенный сервер для большой базы данных: конфигурация и цена.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Сколько реально займёт финальное окно простоя?
При правильно настроенной репликации и лаге, доведённом до нуля перед переключением, — от нескольких секунд до нескольких минут. Если лаг перед началом окна большой (минуты и часы), сначала дождитесь его снижения — не начинайте окно вслепую.
Обязательно ли использовать одинаковую версию СУБД на старом и новом сервере?
Для логической репликации PostgreSQL и для binlog-репликации MySQL совместимость версий имеет значение — репликация между сильно разными мажорными версиями иногда работает, но не гарантирована производителем как основной сценарий. Проверьте матрицу совместимости конкретной версии перед миграцией, особенно если планируете заодно и апгрейд версии СУБД.
Что если во время первичной синхронизации в старой базе меняется схема (добавляются таблицы, колонки)?
DDL-изменения логическая репликация PostgreSQL не переносит автоматически — их нужно применять на новом сервере вручную теми же командами, желательно синхронно с тем, как они применяются на старом. Проще всего на время миграции заморозить любые миграции схемы у приложения.
Можно ли обойтись без репликации, если база «всего» 50 ГБ?
Скорее да — на современном канале и SSD дамп-восстановление 50 ГБ может уложиться в 20-40 минут, и вопрос в том, приемлемо ли это окно для вашего сервиса ночью. Если да — не усложняйте себе жизнь репликацией ради разовой миграции.
Нужно ли останавливать сам сервер базы данных на время финального окна?
Нет, останавливать нужно именно запись — сам процесс СУБД продолжает работать и обслуживать чтение, если это допустимо логикой вашего приложения. Часто проще полностью перевести приложение в режим обслуживания, чем городить частичный read-only на уровне базы.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →