Миграции БД при деплое: Flyway, goose, EF и обратная совместимость
↑ 7.6 DevOps для бэкенд-разработчика · ← Предыдущая · → Следующая
Зачем это на собесе
Как менять схему без простоя и без риска, что старая версия кода сломается.
Объяснение
| Инструмент | Особенности |
|---|---|
| Flyway (Java, CLI, контейнер) | SQL-файлы V1__init.sql, версионирование, чексуммы |
| goose (Go) | SQL/Go-миграции, up/down |
| EF Core Migrations | C#-миграции, idempotent-скрипты, bundle (см. Миграции EF Core) |
| Liquibase, DbUp, Atlas | альтернативы |
Правила выкладки:
- Миграции отдельным шагом до запуска новой версии (init-контейнер/job), а не из каждого экземпляра приложения.
- Миграции идемпотентны и версионированы, применённые файлы не редактируются.
- Хранятся в репозитории рядом с кодом.
Обратная совместимость (expand → migrate → contract)
- Expand: добавить новое (колонка nullable, новая таблица) — старый код работает.
- Выкатить код, пишущий в старое и новое (или читающий новое).
- Migrate: перенести данные пакетами.
- Contract: после полного перехода удалить старое отдельной миграцией.
| Операция | Безопасно? |
|---|---|
| ADD COLUMN NULL | да |
| ADD COLUMN NOT NULL без default | нет (блокирует, ломает старый код) |
| RENAME/DROP COLUMN | нет: через expand/contract |
| CREATE INDEX | в PostgreSQL — CONCURRENTLY |
| Изменение типа | пересоздание колонки поэтапно |
Нюансы и подводные камни
- Долгие блокировки (
ALTER TABLEна больших таблицах) останавливают прод. - Откат «вниз» миграции данных часто невозможен: планируйте прямые исправления.
- Одновременная работа двух версий приложения при rolling-обновлении требует совместимости.
- Тестируйте миграции на копии реальных объёмов данных.
- Блокировка на уровне миграций (advisory lock) предотвращает параллельный запуск.
Практика
- Переименуйте колонку через expand/contract на тестовой БД.
- Настройте job миграций в CI/CD перед деплоем.
- Создайте индекс на большой таблице без блокировки.
Вопросы с ответами
Как менять схему без простоя?
Обратно совместимыми шагами (expand/contract) и короткими неблокирующими операциями.
Почему не применять миграции из приложения при старте?
Гонки между репликами, долгие DDL при старте и сложный откат; лучше отдельный контролируемый шаг.