Миграция схемы БД без простоев: Expand-Contract паттерн

Изменение схемы базы данных часто приводит к простоям и блокировкам, что критично для бизнеса. Мы применяем проверенный паттерн Expand-Contract, чтобы провести миграцию без остановки работы приложения. Наша команда реализует проект под ключ — от аудита до поддержки, обеспечивая надёжность и безопасность изменений.
Часто задаваемые вопросы

Zero-downtime миграция схемы БД: паттерн Blue-Green

Самая распространённая причина downtime при деплое — изменение схемы БД. ALTER TABLE на таблице в 200 млн строк в PostgreSQL держит AccessExclusiveLock несколько минут. Всё это время запросы на запись висят в очереди, connection pool забивается, приложение отдаёт 500. Для высоконагруженного e-commerce такой простой может стоить около $9k–13k. Мы специализируемся на zero-downtime миграциях и используем Expand-Contract — единственный надёжный паттерн, проверенный на таблицах объёмом более 1 ТБ. Он позволяет менять схему без остановки приложения. В e-commerce каждая минута простоя стоит до $5 000, поэтому критически важно избежать блокировок.

Blue-green database migration — это не две отдельные базы данных с синхронизацией. В реальности это расширение схемы так, чтобы одновременно работали и старая, и новая версия приложения, читая из одной БД. Мы предлагаем полный цикл: аудит, проектирование, реализацию и тестирование. Получите консультацию по zero-downtime миграции — свяжитесь с нами для оценки вашего проекта.

Почему Expand-Contract — единственный надёжный паттерн?

Фундаментальный принцип zero-downtime миграции схемы называется Expand-Contract (или Parallel Change). Он состоит из трёх фаз, каждая из которых деплоится отдельно.

Фаза Expand — расширяем схему, не ломая старый код:

-- Добавляем новый столбец как nullable (!)
ALTER TABLE orders ADD COLUMN payment_method VARCHAR(64);

-- Или переименование через добавление нового столбца
ALTER TABLE users ADD COLUMN first_name VARCHAR(255);
ALTER TABLE users ADD COLUMN last_name VARCHAR(255);

-- Индекс создаём CONCURRENTLY — без блокировки таблицы
CREATE INDEX CONCURRENTLY idx_orders_payment_method ON orders(payment_method);

После этого деплоится версия приложения v2, которая пишет и в старые, и в новые столбцы одновременно. Старая версия v1 продолжает работать — она не знает о новых столбцах, но это не проблема, потому что они nullable.

Фаза Migration — бэкфилл данных в фоне:

-- Заполняем новые столбцы батчами, не блокируя таблицу
DO $$
DECLARE
    batch_size INT := 10000;
    last_id BIGINT := 0;
    max_id BIGINT;
BEGIN
    SELECT MAX(id) INTO max_id FROM users;
    WHILE last_id < max_id LOOP
        UPDATE users
        SET first_name = split_part(name, ' ', 1),
            last_name = trim(substr(name, strpos(name, ' ')))
        WHERE id > last_id AND id <= last_id + batch_size AND first_name IS NULL;
        last_id := last_id + batch_size;
        PERFORM pg_sleep(0.1); -- небольшая пауза между батчами
    END LOOP;
END $$;

Фаза Contract — убираем старое после полного перехода:

-- Только после того, как v1 полностью выведена из эксплуатации
ALTER TABLE users DROP COLUMN name;
ALTER TABLE orders ALTER COLUMN payment_method SET NOT NULL;

Где это ломается: типичные ошибки и их решения

Самая частая ошибка — добавить NOT NULL без DEFAULT в Expand фазе. PostgreSQL до версии 11 при этом переписывал всю таблицу. С PostgreSQL 11+ ADD COLUMN NOT NULL DEFAULT 'value' работает мгновенно (значение хранится в системном каталоге), но только если DEFAULT статический, а не вызов функции.

Вторая ловушка — уникальные индексы. CREATE UNIQUE INDEX CONCURRENTLY может завершиться с ошибкой duplicate key, если данные уже содержат дубли. Тогда индекс остаётся в состоянии INVALID — его надо дропнуть и разобраться с данными прежде, чем создавать снова.

Третья проблема — внешние ключи. ALTER TABLE ADD CONSTRAINT FOREIGN KEY в PostgreSQL берёт ShareRowExclusiveLock на обе таблицы и полностью сканирует дочернюю таблицу. На больших таблицах это займёт минуты. Решение — создавать FK с NOT VALID, потом валидировать отдельно:

-- Шаг 1: добавить FK без проверки существующих строк (быстро)
ALTER TABLE orders ADD CONSTRAINT fk_orders_users FOREIGN KEY (user_id) REFERENCES users(id) NOT VALID;
-- Шаг 2: валидация в фоне (не блокирует DML, только берёт ShareUpdateExclusiveLock)
ALTER TABLE orders VALIDATE CONSTRAINT fk_orders_users;

Как избежать блокировок при добавлении внешних ключей?

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

Автоматизация проверок в CI/CD

В pipeline перед деплоем стоит запускать статический анализ миграций. Инструмент squawk (для PostgreSQL) проверяет SQL-файлы на опасные паттерны:

# .github/workflows/db-migration-check.yml
- name: Check migrations safety
  run: |
    squawk db/migrations/*.sql \
      --exclude=prefer-text-field \
      --pg-version=14.0

Squawk поймает ADD COLUMN NOT NULL без DEFAULT, DROP INDEX без CONCURRENTLY, ALTER TABLE RENAME и другие операции, требующие AccessExclusiveLock. По сравнению с ручным ревью, Squawk находит 90% проблем в 10 раз быстрее.

Для автоматического тестирования самой миграции — поднять копию production БД (через pg_dump/restore или логическую репликацию) и прогнать миграцию там перед применением на prod.

Чек-лист для безопасной миграции
  • Проверить, что нет NOT NULL без DEFAULT в Expand
  • Убедиться, что уникальные индексы создаются CONCURRENTLY и нет дублей
  • Для FK использовать NOT VALID + отдельная валидация
  • Запустить squawk перед деплоем
  • Протестировать на копии production
  • Подготовить скрипты отката
Проверка Чем опасна Как детектит squawk
ADD COLUMN NOT NULL без DEFAULT Блокирует таблицу на всё время добавления ✅
DROP INDEX без CONCURRENTLY Блокирует таблицу ✅
ALTER TABLE RENAME Требует AccessExclusiveLock ✅
ADD FOREIGN KEY без NOT VALID Сканирует таблицу под ShareRowExclusiveLock ⚠️ (требует дополнительного анализа)

Что входит в нашу работу

При заказе blue-green миграции мы предоставляем:

  1. Аудит текущей схемы — выявление узких мест, зависимостей, размеров таблиц.
  2. План миграции — декомпозиция на фазы Expand-Contract с временными оценками.
  3. Реализация миграций — написание SQL-скриптов, бэкфилл-процедур, скриптов отката.
  4. Тестирование — прогон на staging с объёмом, сопоставимым с production.
  5. Документация — описание процесса, runbook для операторов, postmortem-шаблон.
  6. Поддержка после деплоя — мониторинг ошибок, наблюдение за метриками базы данных.

Процесс реализации

Этап Содержание Срок
Аудит схемы Анализ текущих таблиц, размеров, типов изменений 1 день
Проектирование Декомпозиция изменений на фазы Expand-Contract 1-2 дня
Реализация Написание миграций, бэкфилл-скриптов 2-5 дней
Тестирование Прогон на staging с production-объёмами данных 1-2 дня
Деплой Поэтапный rollout по фазам По расписанию

Для простых изменений (добавление nullable столбца, новый индекс) — 2-3 дня с тестированием. Для сложных переструктурирований таблиц с десятками миллионов строк — до 2-3 недель включая бэкфилл и мониторинг.

Оценим ваш проект за один рабочий день. Напишите нам — подберём оптимальный план миграции с гарантией zero-downtime. Наш опыт — десятки успешных миграций на PostgreSQL в highload-проектах. Свяжитесь с нами, чтобы обсудить вашу задачу.

Согласно документации PostgreSQL, ALTER TABLE с NOT VALID минимизирует блокировки (PostgreSQL Documentation).

Наши компетенции:

Последние работы

  • Разработка сайта компании B2B ADVANCE
    Разработка сайта компании B2B ADVANCE
    1542
  • Разработка веб-приложения для компании FEEDME
    Разработка веб-приложения для компании FEEDME
    1393
  • Разработка веб-сайта для компании БЕЛФИНГРУПП
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    1081
  • Разработка интернет магазина для компании FURNORO
    Разработка интернет магазина для компании FURNORO
    1368
  • Разработка логотипа компании B2B Advance
    Разработка логотипа компании B2B Advance
    815
  • Разработка веб-приложения для компании Enviok
    Разработка веб-приложения для компании Enviok
    1139