Как происходит изменение структуры данных в базе
Что проверяют
Понимаете ли вы, что миграция это не «накатили ALTER», а совместимость работающего кода со схемой в момент выката. Плюс знание цены конкретных DDL: какие из них мгновенны, а какие переписывают таблицу под эксклюзивной блокировкой.
Как отвечать
- Схема версионируется файлами в репозитории, накат автоматический и учитываемый: golang-migrate, goose, Atlas. Применённые версии хранятся в служебной таблице, файл после мержа не переписывается.
- Базовый приём expand/contract. Сначала расширяем схему совместимо (новая колонка nullable), выкатываем код, пишущий в старое и новое поле, бэкофиллим пачками, переключаем чтение и только отдельным релизом удаляем старое. Переименование колонки одним шагом гарантированно ломает работающие поды во время rolling update.
- Цена DDL в PostgreSQL:
ADD COLUMNбез volatile-значения по умолчанию мгновенен, меняются только метаданные (начиная с версии 11 это верно и дляADD COLUMN ... DEFAULT). А смена типа,SET NOT NULLна большой таблице иVACUUM FULLпереписывают её целиком под ACCESS EXCLUSIVE. - Индексы на живой базе только через
CREATE INDEX CONCURRENTLY, и он не работает внутри транзакции: в golang-migrate такую миграцию помечают как выполняемую без транзакции. - Перед DDL обязателен
lock_timeout. ALTER ждёт блокировку в очереди и своим ожиданием останавливает все последующие запросы к таблице. Лучше упасть через секунду и повторить. - Откат в проде это roll forward. Down-миграция, удаляющая колонку с данными, применима только на dev.
Где спотыкаются
Считают резервную копию планом отката. Восстановление боевой базы занимает часы, а данные, накопленные после сбоя, теряются.
Катят DDL без lock_timeout и получают лавину ожиданий: один ALTER за долгой транзакцией останавливает весь трафик к таблице.
Бэкофиллят одним UPDATE на всю таблицу. На миллионах строк это раздувает WAL, держит долгую транзакцию и блокирует автовакуум.
Меняют схему и код в одном релизе, без совместимого промежуточного шага, поэтому во время выката часть реплик работает со старым кодом и падает.