Миграции схемы базы данных
Зачем менять схему версионированными миграциями, чем up отличается от down и как это вписать в деплой без даунтайма.
Зачем версионировать схему
Код в git версионируется, а схема базы (таблицы, колонки, индексы)
меняется руками через psql. Тогда непонятно, какая схема на каком
сервере, новый разработчик не может поднять базу с нуля, а откат
невозможен. Миграции решают это: каждое изменение схемы это
пронумерованный файл в репозитории, рядом с кодом.
Миграция — файл с номером
Миграция описывает одно изменение схемы и имеет порядковый номер или timestamp. Инструмент (Flyway, Liquibase, Alembic, golang-migrate и подобные) хранит в самой базе таблицу применённых миграций и накатывает только новые.
migrations/
0001_create_users.sql
0002_add_email_index.sql
0003_add_active_column.sql
-- 0003_add_active_column.sql
ALTER TABLE users ADD COLUMN active boolean NOT NULL DEFAULT true;
Запуск применяет все непринятые миграции по порядку:
$ migrate up
applied 0002_add_email_index
applied 0003_add_active_column
database is at version 3
База помнит, что версия 3, и повторный запуск ничего не делает. Это та же идемпотентность из прошлого трека: накатить дважды безопасно.
up и down
У миграции две стороны. up применяет изменение, down откатывает его. Добавили колонку в up, в down её удаляют.
-- up
ALTER TABLE users ADD COLUMN phone text;
-- down
ALTER TABLE users DROP COLUMN phone;
down полезен для отката в разработке, но на проде откатывать схему опасно:
down, удаляющий колонку, уничтожит данные. Поэтому на проде чаще катят
только вперёд, а ошибку чинят новой миграцией.
Миграции и деплой без даунтайма
Здесь миграции встречаются с rolling-выкатом из урока про стратегии. Во время выката старая и новая версии кода работают вместе, поэтому схема должна подходить обеим. Опасны разрушающие изменения: удалить колонку, на которую ещё ходит старый код, значит уронить его.
Безопасный приём — менять схему в несколько шагов (expand/contract):
- expand: добавить новое, не ломая старое (новая колонка, nullable).
- выкатить код, который пишет и в старое, и в новое.
- перенести данные, переключить код на новое.
- contract: в следующем релизе удалить старое, когда им никто не пользуется.
Так схема в каждый момент совместима с обеими версиями кода, и выкат проходит без даунтайма.
Что важно запомнить
Миграции версионируют схему вместе с кодом: каждое изменение это пронумерованный файл, инструмент применяет только новые и помнит версию в базе. Накат идемпотентен.
up применяет, down откатывает, но на проде откат схемы опасен (теряются данные), поэтому ошибку чаще исправляют новой миграцией.
Во время rolling-выката схема должна подходить и старой, и новой версии кода. Разрушающие изменения делают поэтапно (expand/contract), чтобы не было даунтайма.
В следующем уроке разберём пулы соединений: как не упереться в лимит подключений к базе.
Прошли материал до конца? Отметьте урок пройденным.
Прогресс сохранён в этом браузере
Почистишь историю или откроешь с телефона, и отметки пропадут. Оставь почту, они переедут в аккаунт. Пароль придумывать не нужно.
Готово. теперь в аккаунте: прогресс виден с любого устройства. Мой прогресс