Представьте: ваша база данных PostgreSQL 13 работает под нагрузкой 10 000 RPS, а вам нужно перейти на PostgreSQL 15 без остановки приложения. Обычный дамп и восстановление — это часы даунтайма. Клиенты потеряны, деньги утекают. Каждый час простоя обходится в сотни тысяч рублей. Мы решаем эту задачу через zero-downtime миграцию — с логической репликацией, батчевым переносом и автоматическим переключением. Наш опыт — более 50 успешных проектов, гарантия целостности данных и индивидуальный план для вашей инфраструктуры.
За последние три года мы провели миграции для проектов с нагрузкой от 1 000 до 50 000 RPS. В этой статье разберём два основных подхода — pg_upgrade и logical replication — и покажем, как безболезненно обновить СУБД с сохранением доступности. Стоимость типового проекта — от 300 000 до 600 000 руб., а экономия от устранения даунтайма может превышать 500 000 руб. за час. Закажите консультацию — и мы подготовим индивидуальный план миграции за 1 день.
Принципы zero-downtime миграций
Любое изменение схемы БД проходит через backward-compatible этапы:
- Добавить новое (колонку, таблицу) — приложение игнорирует новое.
- Задеплоить код, который пишет в оба места.
- Мигрировать существующие данные батчами.
- Задеплоить код, который читает только из нового.
- Удалить старое.
Никаких DROP COLUMN и RENAME COLUMN в production за один шаг.
Как выполнить zero-downtime миграцию PostgreSQL?
Способ 1: pg_upgrade с репликой
pg_upgrade быстрее logical replication в 10 раз по времени выполнения, но требует короткого даунтайма для подготовки и не позволяет отката. "pg_upgrade uses hard links to avoid copying data, making it extremely fast."
# 1. Поднять новую версию PostgreSQL рядом
apt install postgresql-15
# 2. Остановить запись (короткий даунтайм для подготовки)
pg_ctl -D /var/lib/postgresql/14/main stop
# 3. pg_upgrade в режиме --link (без копирования файлов)
/usr/lib/postgresql/15/bin/pg_upgrade \
--old-datadir=/var/lib/postgresql/14/main \
--new-datadir=/var/lib/postgresql/15/main \
--old-bindir=/usr/lib/postgresql/14/bin \
--new-bindir=/usr/lib/postgresql/15/bin \
--link \
--check
# 4. Выполнить upgrade
/usr/lib/postgresql/15/bin/pg_upgrade \
--old-datadir=/var/lib/postgresql/14/main \
--new-datadir=/var/lib/postgresql/15/main \
--old-bindir=/usr/lib/postgresql/14/bin \
--new-bindir=/usr/lib/postgresql/15/bin \
--link
Режим --link использует hardlinks вместо копирования — для базы в 100 ГБ занимает секунды вместо часов. Недостаток: старую версию после этого не запустить.
Способ 2: Logical Replication (настоящий zero-downtime)
-- На старом сервере (PG 13)
CREATE PUBLICATION migration_pub FOR ALL TABLES;
-- На новом сервере (PG 15) — создать ту же схему
pg_dump -s -U postgres myapp | psql -U postgres -h new-server myapp
-- Подписка на репликацию
CREATE SUBSCRIPTION migration_sub
CONNECTION 'host=old-server dbname=myapp user=replication password=pass'
PUBLICATION migration_pub;
-- Следить за прогрессом первоначальной синхронизации
SELECT subname, received_lsn, latest_end_lsn
FROM pg_stat_subscription;
После синхронизации:
-- Проверить лаг (должен быть близок к нулю)
SELECT now() - last_msg_receipt_time AS subscription_lag
FROM pg_stat_subscription;
-- Переключение: остановить запись в старую БД, дождаться лага = 0
-- Обновить connection string в приложении
-- Удалить подписку
DROP SUBSCRIPTION migration_sub;
Сравнение методов миграции
| Метод |
Время выполнения |
Даунтайм |
Откат |
Дополнительные ресурсы |
| pg_upgrade |
Секунды (100 ГБ) |
Да (до 5 мин) |
Нет |
Минимум |
| Logical replication |
Часы (настройка) |
Нет |
Да |
Доп. сервер, диски |
Почему схемные миграции требуют нескольких шагов?
Добавление NOT NULL колонки
Нельзя в один шаг — ALTER TABLE заблокирует таблицу на всё время DEFAULT-вычисления.
Правильно:
-- Шаг 1: добавить колонку с DEFAULT (PostgreSQL 11+ — instant)
ALTER TABLE users ADD COLUMN phone VARCHAR(20) DEFAULT NULL;
-- Шаг 2: заполнить данными батчами
DO $$
DECLARE
batch_size INT := 1000;
offset_val INT := 0;
BEGIN
LOOP
UPDATE users SET phone = '' WHERE id IN (
SELECT id FROM users WHERE phone IS NULL ORDER BY id LIMIT batch_size
);
EXIT WHEN NOT FOUND;
PERFORM pg_sleep(0.01);
END LOOP;
END $$;
-- Шаг 3: добавить NOT NULL constraint (быстро, если нет NULL)
ALTER TABLE users ALTER COLUMN phone SET NOT NULL;
Переименование колонки
-- Шаг 1: добавить новую колонку
ALTER TABLE orders ADD COLUMN customer_id BIGINT;
-- Шаг 2: заполнить данными (+ триггер для новых записей)
CREATE OR REPLACE FUNCTION sync_customer_id() RETURNS TRIGGER AS $$
BEGIN
NEW.customer_id := NEW.user_id;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER sync_customer_id_trigger
BEFORE INSERT OR UPDATE ON orders
FOR EACH ROW EXECUTE FUNCTION sync_customer_id();
-- Батчевое заполнение существующих записей
UPDATE orders SET customer_id = user_id WHERE customer_id IS NULL;
-- Шаг 3: задеплоить код, читающий customer_id
-- Шаг 4: удалить старую колонку и триггер
ALTER TABLE orders DROP COLUMN user_id;
DROP TRIGGER sync_customer_id_trigger ON orders;
Инструменты для online миграций
| Инструмент |
СУБД |
Особенности |
| gh-ost |
MySQL |
Online schema migration без блокировок, от GitHub |
| pg-osc |
PostgreSQL |
Аналог gh-ost для Postgres |
| Flyway |
PostgreSQL, MySQL |
Версионирование миграций, поддержка undo |
| Liquibase |
PostgreSQL, MySQL |
Чейнджлоги в XML/YAML/JSON |
Пример использования gh-ost:
gh-ost \
--host=db-master \
--database=myapp \
--table=users \
--alter="ADD INDEX idx_email (email)" \
--execute
Тестирование плана миграции
# Восстановить production dump в staging
pg_restore -U postgres -d myapp_staging production.dump
# Проверить план миграции
psql -U postgres myapp_staging < migration_plan.sql
# Замерить время выполнения
\timing on
\i migration_plan.sql
Дополнительные проверки для logical replication
- Убедитесь, что wal_level = logical на старом сервере.
- Проверьте, что у пользователя replication есть права на публикацию.
- Мониторьте лаг с помощью pg_stat_replication.
Мониторинг и готовность к откату
После запуска логической репликации критично отслеживать лаг репликации и здоровье обоих серверов. Мы настраиваем Prometheus + Grafana с дашбордом для pg_stat_replication: лаг более 100 МБ — сигнал замедлить батчевую миграцию или увеличить ресурсы. Параллельно держим наготове rollback-план: при любой аномалии в течение 30 секунд возвращаем traffic на старый сервер. Типичные метрики для мониторинга: received_lsn vs sent_lsn (лаг по байтам), write_lag, flush_lag и replay_lag в pg_stat_replication. При MySQL — Seconds_Behind_Source из SHOW SLAVE STATUS. Нулевой лаг перед переключением достигается остановкой записи на источнике на 2–5 секунд — это единственный момент «риска». Для крупных БД (от 500 ГБ) предварительная синхронизация через rsync или pg_basebackup ускоряет первоначальную подготовку подписки в 3–5 раз по сравнению с чистой репликацией. Мы документируем каждый шаг в runbook-е и обучаем команду заказчика работать с ним.
Что входит в работу
- Аудит текущей схемы БД и версий СУБД.
- Разработка плана backward-compatible миграции.
- Настройка логической репликации или pg_upgrade.
- Батчевый перенос данных с проверкой целостности.
- Мониторинг лага и автоматическое переключение.
- Документация и обучение команды.
- Гарантия rollback-плана на 30 дней.
Сроки и стоимость
Zero-downtime обновление PostgreSQL с logical replication — 2–3 дня. Комплексная схемная миграция с несколькими шагами — 1–2 недели (включая тестирование на staging). Стоимость рассчитывается индивидуально после оценки вашего проекта. Свяжитесь с нами для бесплатной оценки вашего проекта.
Редизайн и миграция сайта: смена CMS, сохранение SEO
Клиент пришёл через 6 недель после самостоятельного редизайна: «Мы переехали с WordPress на Tilda, трафик упал на 70%». Открываю Google Search Console — 847 страниц отдают 404, URL-структура полностью изменилась, не было ни одного 301-редиректа. Яндекс ещё не переиндексировал новый сайт, позиции рухнули. Восстановление заняло 4 месяца и обошлось в потерю выручки около 2 млн рублей за квартал. Наш опыт — более 7 лет и 80+ успешных миграций, гарантируем сохранение позиций при правильном подходе.
Почему миграции ломают SEO
Поисковики проиндексировали конкретные URL. Если /catalog/shoes/nike-air-max-270 превратился в /products/nike-air-max-270 без 301-редиректа — весь ссылочный вес страницы, весь трафик, все позиции уходят в никуда. Google говорит, что 301 передаёт ~99% PageRank, но на практике позиции восстанавливаются за 2–8 недель, а не мгновенно.
Чаще всего SEO ломают не из злого умысла, а потому что разработчик не думает о URL-структуре как о публичном API. Вот типичные поломки:
| Проблема |
Причина |
Решение |
| Дублированный контент |
Новый сайт открывается параллельно со старым |
Отключить индексацию dev-версии, настроить canonical |
| Потеря метаданных |
Title и description остались в старой CMS |
Экспорт через API, массовый импорт с проверкой |
| Изменение canonical |
Пагинация и фильтры сбросились |
Зафиксировать до разработки, внедрить в шаблон |
| Скорость просела |
Тяжёлые секции, неоптимизированные изображения |
Оптимизировать LCP, CLS, TTFB до запуска |
Как восстановить трафик после неудачной миграции?
Если трафик упал — действуйте немедленно:
- Краул нового сайта на 404 и сравнение с предмиграционным списком URL.
- Создание редиректов для всех потерянных страниц с трафиком >0.
- Проверка структурированных данных и мета-тегов на тестовой выборке.
- Ежедневный мониторинг Coverage в Search Console и позиций по топ-50 запросам.
- Если спустя 2 недели трафик не восстанавливается — глубокий аудит редиректов (транзитивность, цепочки, циклы).
В нашей практике такой случай: крупный интернет-магазин потерял 50% трафика при переезде с Битрикса на React + Strapi. За три дня восстановили 95% редиректов, через 3 недели трафик вернулся на 90% от исходного.
Предмиграционный аудит: что нельзя пропустить
До начала разработки нового сайта нужно:
- Полный краул текущего сайта через Screaming Frog или Sitebulb. Получить список всех индексируемых URL с трафиком из Google Search Console.
- Выгрузить все страницы с органическим трафиком >0 за последние 6 месяцев — это приоритет для редиректов.
- Зафиксировать все внешние ссылки (backlinks) на конкретные страницы — Ahrefs, Semrush.
- Сфотографировать текущие позиции по ключевым запросам — база для сравнения после миграции.
- Сохранить Core Web Vitals из Search Console за предыдущие 90 дней.
Таблица для фиксации:
| Этап аудита |
Инструмент |
Критичность |
| Сбор URL |
Screaming Frog + GSC |
Высокая |
| Трафик по страницам |
Google Analytics / Search Console |
Высокая |
| Внешние ссылки |
Ahrefs / Majestic |
Средняя |
| Позиции |
Яндекс.Wordstat / Serpstat |
Средняя |
| Core Web Vitals |
GSC CrUX |
Высокая |
Свяжитесь с нами для детального предмиграционного аудита — мы поможем выявить все риски и составить план действий.
Маппинг URL и редиректы
Для проекта с 200+ страницами создаём таблицу маппинга: старый URL → новый URL → статус (301, объединён с другой страницей, удалён). Каждая строка проходит проверку: реально ли контент переехал именно сюда.
В Laravel редиректы через конфигурационный файл и middleware, не через .htaccess — это быстрее и управляемо. Для WordPress → Next.js: редиректы настраиваются в next.config.js (статические) и на уровне Nginx/CDN для динамических. Старый .htaccess на shared хостинге с 500+ строками редиректов — особый ад. Каждый редирект проверяется последовательно, производительность падает. Переносим в Nginx map директиву или Redis-кэш для динамического поиска. Подробнее в Wikipedia: HTTP 301.
Миграция контента из разных CMS
WordPress → Headless CMS (Contentful, Strapi, Sanity):
WordPress REST API или WP All Export для экспорта постов, метаполей, медиафайлов. Скрипт миграции на Node.js: парсим экспорт, трансформируем структуру, загружаем через API CMS. Медиафайлы перегружаем в новое хранилище, обновляем ссылки в контенте. Типичная проблема — shortcodes в контенте WordPress ([gallery id="123"]): нужен парсер и трансформация в новый формат.
1С-Битрикс → современный стек:
Битрикс хранит контент в нестандартных таблицах с IBLOCK_ELEMENT_PROPERTY. Прямой SQL-экспорт через phpMyAdmin или Bitrix API. Трансформация — самая долгая часть из-за специфики структуры данных Битрикса.
Тяжёлые WYSIWYG → структурированный контент:
Годы редактирования в FCKEditor/TinyMCE оставляют inline-стили, нестандартные теги, сломанные атрибуты. HTML sanitize + трансформация в Markdown или Portable Text (Sanity) с ручной проверкой проблемных страниц.
| CMS |
Инструменты миграции |
Сложность |
Риски |
| WordPress |
WP All Export, WP-CLI, REST API |
Средняя |
Shortcodes, meta fields |
| 1C-Битрикс |
Bitrix API, SQL-экспорт |
Высокая |
Сложная структура, свойства инфоблоков |
| Joomla |
J2XML, прямая выгрузка из БД |
Высокая |
Устаревшие расширения |
| Tilda/Readymag |
Экспорт через API (ограничен) |
Средняя |
Нет полного доступа к контенту |
SEO-сохранение технических элементов
Структурированные данные (Schema.org) — если на старом сайте были Product, Article, BreadcrumbList разметки, они должны быть и на новом. Google Search Console → Enhancement reports покажут потерю rich snippets.
Sitemap XML: генерируется автоматически, отправляется в GSC через день после запуска. Старый sitemap остаётся до полной переиндексации.
hreflang для мультиязычных сайтов: если теги потерялись при миграции, через несколько недель начнутся конфликты между языковыми версиями в выдаче.
Open Graph и Twitter Card мета-теги — часто забывают при смене шаблона, страницы перестают корректно отображаться при шаринге в соцсетях.
Запуск и мониторинг первых недель
DNS propagation: переключение DNS занимает до 48 часов, планируйте запуск с запасом. Cloudflare как DNS-провайдер — propagation занимает минуты, не часы.
После запуска ежедневно мониторим: Search Console → Coverage (ошибки индексации), Analytics → органический трафик, сравнение с аналогичным периодом прошлого года, краулинг сайта на 404-ошибки.
Первые 2 недели — критический период. Если трафик падает на 30%+ — немедленный аудит редиректов и сравнение с предмиграционным краулом.
Чек-лист на запуск (спойлер)
- [ ] Все 301 редиректы работают и не образуют цепочек
- [ ] Sitemap отправлен в GSC и Яндекс.Вебмастер
- [ ] Прописаны canonical на всех страницах
- [ ] Проверено отображение Open Graph / Twitter Card
- [ ] Скорректированы robots.txt и мета-теги noindex
- [ ] Core Web Vitals в зелёной зоне (LCP <2.5s, CLS <0.1, INP <200ms)
Что входит в работу
Результаты, которые вы получаете:
- План миграции с маппингом URL и редиректов в формате Excel/Google Sheets.
- Настроенные 301 редиректы на серверном уровне (Nginx/Cloudflare/Vercel).
- Перенесённый контент с проверкой целостности: изображения, мета-поля, ссылки.
- Структурированные данные (Schema.org) на новом сайте, идентичные старым или улучшенные.
- Отчёт по SEO: динамика позиций через 1, 3 и 6 недель после запуска.
- Мониторинг Coverage в Search Console с уведомлениями об ошибках.
- Гарантия сохранения позиций: если трафик падает более чем на 15% в течение первого месяца — бесплатный аудит и коррекция.
Сроки и ориентиры
- Редизайн с миграцией небольшого сайта (до 100 страниц): 4–8 недель.
- Миграция e-commerce с 500+ страниц товаров: 8–16 недель.
- Только техническая часть миграции (редиректы, метаданные) без редизайна: 1–3 недели.
Стоимость рассчитывается индивидуально по объёму. Средняя экономия клиента за счёт сохранения трафика после миграции — от 300 000 до 500 000 рублей в год.
Получите консультацию по вашему проекту — мы ответим в течение дня. Закажите предмиграционный аудит вашего сайта и получите точную смету с планом редиректов. Свяжитесь с нами, чтобы обсудить детали.