Миграции базы данных: как не взорвать продакшен и сохранить нервы
Вступление: почему миграции — это не просто ALTER TABLE
Помните первый раз, когда вы запустили ALTER TABLE users ADD COLUMN phone VARCHAR(20) в продакшене? И сразу же поняли, что забыли про индексы, триггеры и 500 миллионов записей? Миграции базы данных — это не просто версионирование схемы. Это система, которая должна учитывать:
- Производительность: как
ALTER TABLEна 100ГБ таблице отразится на запросах пользователей? - Откаты: что делать, если миграция упала на середине?
- Данные: как обновить миллионы строк без блокировок?
- Совместимость: как гарантировать, что новая схема не сломает приложение до следующего релиза?
Без правильного подхода миграции становятся источником стресса, а не инструментом для улучшения архитектуры.
Проблема: когда миграции превращаются в кошмар
Вот реальные сценарии, с которыми сталкиваются команды:
Блокировки и тайм-ауты:
-- Так делать нельзя: это убивает производительность на больших таблицах ALTER TABLE orders ADD COLUMN status VARCHAR(50);- Решение: использовать
pt-online-schema-change(Percona Toolkit) или аналогичные инструменты для онлайн-миграций.
- Решение: использовать
Неявные зависимости:
- Разработчик добавляет поле в таблицу, а через неделю оказывается, что его использует 3-й сервис, о котором никто не знал.
- Решение: автоматизировать проверку зависимостей (например, с помощью
pg_dependв PostgreSQL илиinformation_schemaв MySQL).
Отсутствие откатов:
- Миграция прошла на половину, а затем упала. Как вернуться назад без потери данных?
- Решение: всегда писать откатные миграции и тестировать их отдельно.
"Ручные" миграции:
- Админ запускает SQL-скрипт вручную, забывает записать версию, и через месяц никто не понимает, что уже сделано.
- Решение: использовать инструменты типа Flyway, Liquibase или собственные скрипты с версионированием.
Практика: как правильно мигрировать
1. Инструменты и workflow
Выбор инструмента зависит от СУБД и масштаба проекта. Вот проверенные варианты:
Flyway (рекомендуется для средних проектов)
Преимущества: простой, интегрируется с CI/CD, поддерживает откаты.
Пример конфигурации
flyway.conf:flyway.url=jdbc:postgresql://db-prod:5432/app_db flyway.user=deploy_user flyway.password=secure_password flyway.locations=filesystem:db/migration flyway.baselineOnMigrate=true # Если миграции уже есть в продакшене flyway.outOfOrder=false flyway.validateOnMigrate=trueСтруктура миграций:
db/migration/ ├── V1__Add_users_table.sql ├── V2__Add_index_to_users.sql ├── V3__Add_phone_column.sql └── V3_down__Remove_phone_column.sql # Откат
Liquibase (лучше для сложных схем)
- Преимущества: поддерживает XML/YAML/JSON, удобно для больших схем.
- Пример XML-миграции:
<changeSet id="2" author="dev-team"> <addColumn tableName="orders"> <column name="status" type="varchar(50)" defaultValueNull="true"/> </addColumn> </changeSet>
Ручное управление (только для мелких проектов)
- Использовать скрипты с версионированием и проверкой статуса:
# Проверка, что миграция не выполнена if ! psql -U user -d db -c "SELECT * FROM schema_migrations WHERE migration = 'V3__Add_phone';"; then psql -U user -d db -f migrations/V3__Add_phone.sql psql -U user -d db -c "INSERT INTO schema_migrations (migration) VALUES ('V3__Add_phone');" fi
2. Стратегии миграций
| Стратегия | Когда использовать | Риски | Инструменты |
|---|---|---|---|
| Offline | Маленькие таблицы (<10М записей) | Блокировки на время миграции | ALTER TABLE |
| Online (PTOSC) | Большие таблицы (>100М записей) | Высокая нагрузка на CPU/Disk | pt-online-schema-change |
| Zero-downtime | Критические системы | Сложность реализации | Gh-ost (MySQL), pg_repack (PostgreSQL) |
| Двойная запись | Миграции с изменением логики данных | Удвоение хранения | Custom scripts + приложение |
Пример онлайн-миграции с pt-online-schema-change (MySQL)
pt-online-schema-change \
--alter "ADD COLUMN status VARCHAR(50)" \
--table users \
--execute \
--alter-foreign-keys-method=auto \
--max-lag 0.1 \
--statistics \
--ask-pass
- Ключевые параметры:
--max-lag: максимальная задержка репликации (в секундах).--alter-foreign-keys-method: как обрабатывать внешние ключи.
3. Тестирование миграций
Никогда не доверяйте миграциям "на глаз". Используйте:
Локальное окружение:
- Запускайте миграции на копии продакшен-данных (например, с помощью
pg_dumpилиmysqldump --where).
- Запускайте миграции на копии продакшен-данных (например, с помощью
CI/CD проверки:
- Добавляйте в пайплайн шаг валидации миграций:
# Пример для GitHub Actions - name: Validate migrations run: | flyway validate \ -url=jdbc:postgresql://localhost:5432/test_db \ -user=test_user \ -password=test_passРолинг-бэк тесты:
- Проверяйте откатные миграции отдельно:
flyway repair # Удаляет метки выполненных миграций flyway migrate # Запускает миграции заново flyway migrate -X=undo # Пробует откатить последнюю
Типичные ошибки и как их избежать
Отсутствие версионирования
- Проблема: Нет контроля над тем, какие миграции уже выполнены.
- Решение: Использовать таблицу
schema_migrations(Flyway) или аналогичную.
Большие транзакции
- Проблема:
BEGIN; ALTER TABLE big_table ADD COLUMN new_field; UPDATE big_table SET new_field = 'default'; COMMIT;- Блокирует таблицу на часы.
- Решение: Разбивать на маленькие транзакции или использовать онлайн-инструменты.
- Проблема:
Игнорирование индексов
- Проблема: Добавление поля без индекса приводит к падению производительности запросов.
- Решение: Автоматически добавлять индексы в миграциях или документировать их в отдельном файле.
Отсутствие откатов
- Проблема: Если миграция падает, приходится руками чистить данные.
- Решение: Всегда писать откатные скрипты и тестировать их.
"Живые" миграции в продакшене
- Проблема: Разработчик запускает миграцию в 3 часа ночи без предупреждения.
- Решение: Использовать чеклисты и approval-процессы (например, через Argo Workflows или manual_job в GitLab CI).
Практический вывод: checklist для безопасных миграций
Перед миграцией:
- Проверить зависимость от других сервисов (граф зависимостей).
- Создать бэкап данных (даже если думаете, что не нужен).
- Оценить время блокировки таблиц (и предупредить команду).
Во время миграции:
- Запускать в отдельной транзакции для критичных изменений.
- Мониторить нагрузку на базу (CPU, диск, latency).
- Иметь под рукой откатный план (даже если миграция кажется простой).
После миграции:
- Проверить логи приложения на ошибки.
- Запустить нагрузочные тесты (если миграция касалась производительности).
- Обновить документацию (схемы, API-контракты).
Для команды:
- Документировать каждую миграцию (что изменилось, почему, риски).
- Обучать новых членов команды процедурам миграций.
- Регулярно ревьюить миграционные скрипты (как код).
Заключение: миграции — это инженерная задача
Миграции базы данных — это не просто SQL-команды. Это процесс, который требует внимания к деталям, тестирования и понимания последствий. Используйте проверенные инструменты, автоматизируйте рутинные задачи и никогда не запускайте миграции "на авось".
Помните: одна плохо продуманная миграция может испортить день не только вам, но и всем, кто зависит от этой базы данных. Будьте аккуратны, тестируйте и всегда держите план Б.