🚀 Как уронить прод одной миграцией
Выкатываешь релиз, в нём миграция - вроде добавить колонку, ерунда
А прод на сорок секунд встаёт колом, запросы висят, алерты орут
Миграции на живой базе - это минное поле, где безобидный ALTER TABLE блокирует всю таблицу
Обо что спотыкаются и как катить схему без даунтайма
🔒 Почему прод встаёт
Многие операции над таблицей берут блокировку, и пока она держится, все запросы к таблице ждут
На маленькой таблице это миллисекунды, а на большой - секунды и десятки секунд, в течение которых сайт фактически лежит
Три главных нарушителя:
- Добавление колонки с NOT NULL и значением по умолчанию
В старых версиях баз это переписывало всю таблицу целиком, с полной блокировкой
Чем больше строк - тем дольше стоишь
- Создание индекса обычным CREATE INDEX - блокирует запись в таблицу на всё время построения
- Переименование или удаление колонки, на которую ещё смотрит работающий код
И самое коварное: во время деплоя одновременно крутятся и старая, и новая версия приложения
Схема уже поменялась, а половина инстансов ещё живёт по-старому
Если миграция ломает старый код — часть юзеров ловит ошибки прямо во время выката
🧩 Главный принцип: expand → migrate → contract
Идея в том, чтобы никогда не менять схему одним резким движением, а разбить на шаги, где на каждом и старый, и новый код живут спокойно:
Expand — расширяешь схему обратимо: добавляешь новое, ничего не ломая
Новая колонка — обязательно nullable, без жёстких констрейнтов
Migrate — переносишь данные и переключаешь код на новое
Бэкфилл делаешь пачками, а не одним UPDATE на миллион строк (иначе снова блокировка и распухание)
Contract — только когда всё переехало и старый код мёртв, убираешь лишнее и навешиваешь констрейнты
Между шагами катятся отдельные релизы
Да, дольше, зато без даунтайма
🛠 Конкретные приёмы — Индексы строй
CREATE INDEX CONCURRENTLY — он не блокирует запись, строится в фоне
Медленнее, но прод жив
- Колонку добавляй сначала nullable, потом бэкфилли данные пачками, и только потом, отдельным шагом, вешай NOT NULL.
- Колонки не переименовывай на живой базе
Добавь новую, дублируй запись в обе, переведи чтение на новую, старую убери потом
Прямой RENAME мгновенно рассинхронит схему и работающий код
- Удаление - тоже в два захода: сперва перестань использовать в коде, выкати, убедись что ничего не отвалилось, и только следующим релизом дропай из базы
Общая мысль: код всегда должен уметь работать и со старой, и с новой схемой одновременно — потому что в момент деплоя ровно так и происходит
Пляши от этого, и миграции перестанут ронять прод
А вас миграция когда-нибудь роняла на проде? На чём именно поймали — индекс, NOT NULL, переименование? 🤔
#backend #database #postgres #devops #dev #sql
Post #1534
75
- 👍 1