Перейти к содержанию
Innovialabs

Миграции базы данных: как безопасно менять схему

Практика3 мин

Коротко

Меняйте схему только через пронумерованные миграции в репозитории, применяйте их автоматически при деплое и разбивайте опасные изменения на совместимые шаги, чтобы старый и новый код работали с базой одновременно.

Короткий ответ

Миграция — это файл с изменением схемы (создать таблицу, добавить колонку, индекс), у которого есть номер версии. Инструмент хранит в базе служебную таблицу с уже применёнными версиями и при запуске выполняет только новые файлы по порядку.

Правила безопасной работы:

  • схему никогда не меняют руками на сервере — только через миграцию в Git;
  • применённую миграцию не редактируют, а пишут новую;
  • одна и та же цепочка миграций прогоняется на dev, staging и production;
  • опасные изменения разбиваются на несколько шагов.

Какой инструмент выбрать

ИнструментЭкосистемаКак описываются миграции
FlywayJava, любая через CLISQL-файлы V1__init.sql, V2__add_orders.sql
LiquibaseJava, любая через CLIchangelog в XML/YAML/JSON или SQL, есть встроенные rollback-блоки
Prisma MigrateNode.js / TypeScriptменяете schema.prisma, инструмент генерирует SQL
AlembicPython / SQLAlchemyPython-файлы с функциями upgrade() и downgrade()

Выбирайте инструмент вашего стека: ORM-проекты обычно используют родной механизм (Prisma, Alembic, миграции Django или Laravel). Flyway и Liquibase удобны, когда база общая для нескольких сервисов на разных языках.

Пример Alembic-миграции:

def upgrade():
    op.add_column("users", sa.Column("phone", sa.String(20), nullable=True))

def downgrade():
    op.drop_column("users", "phone")

Как менять схему без простоя

Во время деплоя какое-то время работают и старая, и новая версия приложения. Поэтому каждая миграция должна быть обратно совместимой со старым кодом. Для этого используют схему expand / contract.

Пример: переименовать колонку name в full_name.

  1. Expand. Добавьте новую колонку full_name, допускающую NULL.
  2. Код пишет в обе колонки, читает из старой.
  3. Отдельной задачей перенесите старые данные пачками.
  4. Код начинает читать из full_name.
  5. Contract. Следующим релизом удалите колонку name.

Что опасно делать одной миграцией:

  • переименование или удаление колонки, которую читает текущий код;
  • NOT NULL на большой таблице без значения по умолчанию;
  • изменение типа колонки, требующее перезаписи таблицы;
  • создание индекса с блокировкой записи — в PostgreSQL используйте CREATE INDEX CONCURRENTLY.

Стратегии отката

Есть два подхода, и они не взаимоисключающие.

  • Down-миграции (downgrade(), rollback в Liquibase). Удобны на dev, но на production откат удаления колонки не вернёт данные.
  • Roll forward. Ошибку исправляют новой миграцией. Для production это чаще надёжнее.

Минимальная страховка: бэкап перед каждой рискованной миграцией и проверенный план, что делать, если миграция упала посередине. Учтите, что в MySQL DDL-операции не откатываются транзакцией, а в PostgreSQL большинство DDL транзакционны.

Ревью миграций

Миграцию проверяют внимательнее обычного кода, потому что её нельзя просто «откатить коммитом». Чеклист для ревьюера:

  • совместима ли миграция с кодом, который сейчас в production;
  • сколько строк в таблице и будет ли долгая блокировка;
  • есть ли перенос данных внутри DDL-миграции (лучше вынести отдельно);
  • сгенерированный ORM SQL прочитан глазами, а не принят на веру;
  • миграция прогнана на копии с реалистичным объёмом данных.

Как держать окружения в синхроне

  • Миграции применяются автоматически в CI/CD перед запуском новой версии, а не вручную.
  • В CI поднимается чистая база и прогоняются все миграции с нуля — так ловятся сломанные цепочки.
  • Включите проверку дрейфа: Flyway validate, Prisma migrate diff и аналоги показывают расхождение между файлами и реальной схемой.
  • Не допускайте ручных правок схемы на production: если они случились, оформите их миграцией.

Частые ошибки

  • Правка уже применённой миграции — контрольные суммы не сходятся, окружения расходятся.
  • Два разработчика создали миграции с одинаковым номером в разных ветках.
  • Миграция и деплой кода, который от неё зависит, выкатываются в неправильном порядке.
  • Перенос миллионов строк одним UPDATE внутри миграции.

Частые вопросы

Можно для небольших проектов с одним экземпляром. Если экземпляров несколько, лучше выполнять миграции отдельным шагом деплоя, чтобы они не стартовали параллельно; многие инструменты используют блокировку, но отдельный шаг проще контролировать.

Поможем с этим

Если хотите не разбираться самим, а получить результат — этим мы занимаемся каждый день.

Обсудить проект

Читайте также

Есть идея или задача? Расскажите нам

Ответим в течение рабочего дня и предложим решение. Консультация бесплатная.