Skip to content
PПромтбук
RUEN
04База данных

Безопасная миграция БД

Большие изменения схемы без даунтайма и блокировок: backfill, swap, очистка.

Спланируй безопасную миграцию: {{change}}.

Принцип: любое изменение схемы в продакшене — в несколько шагов. Никаких "один большой ALTER".

1. Категория изменения

ТипСложностьСтратегия
Добавить nullable колонкуПростаяОдин ALTER
Добавить NOT NULL колонкуСложноМногошаговая
Переименовать колонкуСложноЧерез duplicate
Изменить типСложноЧерез duplicate
Удалить колонкуПростая (в плане SQL)Многошаговая (код перестроить)
Добавить foreign keyСредняяCONCURRENTLY
Добавить индексСредняяCONCURRENTLY
Разбить таблицуОчень сложноМногоэтапная

2. Pattern: добавить NOT NULL колонку

Шаг 1: ALTER TABLE ... ADD COLUMN x text NULL  (nullable!)
Шаг 2: deploy код который ПИШЕТ в x но не требует
Шаг 3: backfill старых строк (batch'ами!)
Шаг 4: deploy код который ЧИТАЕТ из x
Шаг 5: ALTER TABLE ... ALTER COLUMN x SET NOT NULL (после проверки что нет NULL)

Каждый шаг — отдельный deploy. Между ними можно откатиться.

3. Pattern: переименовать колонку

Шаг 1: ALTER TABLE ... ADD COLUMN new_name (тип)
Шаг 2: deploy: код пишет В ОБА (старое + новое)
Шаг 3: backfill: copy oldnew
Шаг 4: deploy: код читает из new
Шаг 5: deploy: код не пишет в old
Шаг 6: ALTER TABLE ... DROP COLUMN old (через несколько дней)

Никогда не делай ALTER ... RENAME в одной транзакции с код-деплоем.

4. Pattern: backfill больших таблиц

Не делай:

UPDATE huge_table SET x = ...; -- 10 минут lock на всё

Делай batches:

-- Скрипт повторяется
UPDATE huge_table
SET x = ...
WHERE id IN (
  SELECT id FROM huge_table
  WHERE x IS NULL
  LIMIT 1000
);

С pg_sleep(0.1) между батчами чтобы не задушить БД.

5. Pattern: индексы без даунтайма

-- Используй CONCURRENTLY
CREATE INDEX CONCURRENTLY idx_foo ON huge_table (foo);

-- НЕ внутри транзакции!
-- Может занять часы — это нормально

Если индекс не создался (CONCURRENTLY может оставить invalid index) — дропни и пересоздай.

6. Pattern: удалить колонку

Шаг 1: deploy: код больше не использует
Шаг 2: подожди неделю чтобы убедиться что никто не использует
Шаг 3: ALTER TABLE ... DROP COLUMN

Не торопись с DROP — DROP моментален но если ошибся — данные потеряны.

7. Locking

Каждый ALTER берёт lock. Узнай какой:

  • ALTER TABLE ... ADD COLUMN nullable — AccessExclusive, но быстрый
  • ALTER TABLE ... ADD CONSTRAINT — может быть долгий (валидация)
  • ALTER TABLE ... ALTER COLUMN TYPE — rewrite таблицы, очень долго

Для долгих:

SET lock_timeout = '5s';
ALTER TABLE ...;

Если не успело — отваливается, не блокирует прод.

8. Откат

Для каждой миграции — план отката:

  • Можно ли откатить SQL? (DROP COLUMN — нельзя, данные потеряны)
  • Какие коммиты в коде надо откатить?
  • В каком порядке?

9. Тестирование

  • Прогон на staging с продакшен-волюмом
  • Замеры lock и времени
  • Smoke-тест после миграции

10. Чек-лист на запуск

  • Migration script готов
  • Rollback script готов (или отмечено что нельзя откатить)
  • Тестировано на staging
  • Time-окно низкой нагрузки выбрано
  • Кто-то на линии для мониторинга
  • Метрики БД (locks, query duration) видны на дашборде

Анти-паттерны

  • ALTER TABLE x ALTER COLUMN y TYPE int USING y::int на 100M строк
  • ❌ Code deploy + migration в одной транзакции
  • ❌ Backfill единым UPDATE
  • ❌ Удаление колонки на следующий день после прекращения использования
  • ❌ Миграция без отката (для критичных изменений)

В конце дай

  • Пошаговый план с конкретным SQL
  • Estimated lock duration на каждом шаге
  • Откатный план
  • Чек-лист на запуск
К подразделу «База данных»
Похожие промты