Зачем версионировать схему БД: подходы state-based и migration-based

↑ 5.1 Миграции: EF Core, Flyway, goose · → Следующая

Желательно~4 мин чтенияУровень: middle

Зачем это на собесе

Схема БД — часть кода. Спрашивают, как вы гарантируете одинаковую структуру на всех окружениях и как выкатываете изменения.

Проблема

Без контроля версий схемы:

  • разработчики вносят изменения вручную, окружения расходятся (dev ≠ stage ≠ prod);
  • нельзя воспроизвести БД с нуля, нет истории изменений;
  • релизы требуют ручных скриптов, растёт риск ошибок и простоя;
  • невозможно корректно откатить или проанализировать инцидент.

Решение: схема описывается в репозитории и применяется автоматически в пайплайне.

Подходы

Migration-based (на основе миграций)

Хранится последовательность скриптов (миграций). Каждая переводит схему из версии N в N+1. БД запоминает, какие применены, в служебной таблице.

V1__create_customers.sql
V2__add_orders.sql
V3__add_orders_status_index.sql

Плюсы: точный контроль изменений, работа с данными (бэкфилл), явные безопасные шаги, подходит для прода с существующими данными. Минусы: состояние схемы нужно «собирать» из истории, конфликты версий при ветках.

Инструменты: Flyway, Liquibase, EF Core Migrations, goose, dbmate, Alembic, Sqitch, Atlas (versioned).

State-based (на основе состояния)

Хранится желаемое состояние (DDL целевой схемы). Инструмент сам вычисляет разницу между целевой схемой и БД и генерирует изменения.

Плюсы: всегда видно актуальную схему, меньше конфликтов. Минусы: автогенерация не знает о данных (переименование колонки = drop + add → потеря данных), нужно ревью сгенерированного плана; сложно с миграциями данных.

Инструменты: SSDT/SqlPackage (SQL Server), Atlas (declarative), Skeema, Prisma db push.

Гибрид

State-based для разработки/локально, migration-based для прода; либо Atlas: декларативная схема + генерация версионированных миграций.

Code-first и Database-first

  • Code-first: модель в коде → миграции генерируются (EF Core Add-Migration);
  • Database-first: схема главная, модель генерируется (Scaffold-DbContext).

Принципы хороших миграций

  1. Неизменяемость: применённую миграцию не редактируют, исправление делается новой миграцией (иначе контрольные суммы не совпадут).
  2. Идемпотентность и проверка: скрипты запускаются один раз; защитные конструкции (IF NOT EXISTS) для повторяемых.
  3. Маленькие атомарные шаги: одна миграция — одна понятная задача.
  4. Обратная совместимость с кодом: старая версия приложения должна работать со новой схемой (на время выкатки).
  5. Транзакционность: в PostgreSQL DDL транзакционен (но CREATE INDEX CONCURRENTLY нельзя в транзакции).
  6. Тестирование: прогон миграций с нуля и на копии прод-данных в CI.
  7. Ревью как кода: DBA/коллеги проверяют блокировки и влияние на нагрузку.
  8. Не смешивать изменение схемы и тяжёлую миграцию данных (раздельные шаги).
  9. Автоматизация: применение в CD, а не вручную.

Кто и когда применяет

СпособПлюсыМинусы
При старте приложения (Migrate())простогонки при нескольких инстансах, права DDL у приложения, долгий старт, риск откатов
Отдельный шаг пайплайна (job, init-контейнер)контроль, отдельные права, один разнужно отладить порядок и координацию
Вручную DBAстрогий контрольмедленно, человеческий фактор

Рекомендация: отдельный шаг деплоя под отдельным пользователем с правами DDL; приложение работает с урезанными правами.

Откат (rollback)

  • «down»-миграции часто ненадёжны (потеря данных, невозможность вернуть);
  • практика: roll-forward (исправление новой миграцией) + backward-compatible изменения + бэкап/PITR для катастроф;
  • перед рискованной миграцией: резервная копия, план отката, окно обслуживания.

Вопросы с ответами