Аддитивные изменения схемы базы данных: как добавлять без риска для продакшна
У вас работающее приложение с тысячами активных пользователей. Команде нужно добавить поле с номером телефона в профиль пользователя. Изменение кажется мелким, но одна мысль о выполнении ALTER TABLE на продакшн-базе заставляет всех нервничать. Что если миграция заблокирует таблицу? Что если старый код приложения упадет, потому что не ожидает новой колонки?
Эта ситуация знакома почти каждой инженерной команде, управляющей собственной базой данных. Хорошая новость: одна категория изменений схемы remarkably безопасна, даже под нагрузкой продакшна. Понимание этой категории меняет то, как вы планируете развертывания, координируете работу с командой и оцениваете риски при развитии базы данных.
Что делает изменение аддитивным
Аддитивное изменение — это любая модификация, которая только добавляет что-то новое в схему базы данных, не изменяя и не удаляя ничего из уже существующего. Список прост:
- Добавление новой колонки в существующую таблицу
- Создание новой таблицы
- Добавление нового индекса
- Добавление ограничения, которое не затрагивает существующие данные
Ключевое свойство: вы расширяете схему. Вы не переименовываете колонки, не меняете типы данных, не объединяете таблицы и ничего не удаляете. Старые части схемы остаются в точности такими же.
Почему аддитивные изменения безопасны
Безопасность следует из простого факта: старый код приложения не зависит от того нового, что вы только что добавили. Когда вы добавляете колонку phone в таблицу users, существующий код продолжает читать и писать name, email и password ровно так же, как и раньше. Он понятия не имеет, что появилась новая колонка. Он не пытается ее читать и не пытается в нее писать. Ничего не ломается.
Это неверно для других типов изменений. Если вы переименуете колонку с phone на phone_number, старый код попытается прочитать phone и упадет, потому что этой колонки больше нет. Если вы измените тип колонки с VARCHAR на INT, старый код может записать строковое значение, которое база данных больше не примет. Если вы объедините две таблицы, старые запросы, ссылающиеся на исходную структуру, сломаются.
Аддитивные изменения избегают всех этих проблем, потому что они не трогают ничего, от чего зависит старый код.
Единственное критическое правило
Есть одно условие, которое делает аддитивные изменения безопасными: новая колонка должна быть nullable или иметь значение по умолчанию.
Если вы добавите колонку с NOT NULL и без значения по умолчанию, каждая существующая строка в таблице немедленно нарушит ограничение. База данных отклонит миграцию. В худшем случае миграция выполнится частично, пройдет для одних строк, затем упадет, оставив вас с несогласованными данными, которые сложно очистить.
Стандартный паттерн выглядит так:
ALTER TABLE users ADD COLUMN phone VARCHAR(20) NULL;
Если вы знаете, что колонка в конечном итоге станет обязательной, сначала добавьте значение по умолчанию:
ALTER TABLE users ADD COLUMN phone VARCHAR(20) NOT NULL DEFAULT '';
Со значением по умолчанию каждая существующая строка получает пустую строку. Старый код приложения все еще может читать эту колонку без ошибок. Позже, когда все экземпляры приложения будут обновлены для корректной работы с новой колонкой, вы сможете удалить значение по умолчанию или добавить ограничение NOT NULL в отдельной миграции.
Выполнение аддитивных изменений в рабочее время
Одно из самых больших практических преимуществ аддитивных изменений — их можно выполнять, пока продакшн загружен. В большинстве современных баз данных ALTER TABLE ADD COLUMN с nullable колонкой — это легковесная операция, которая не блокирует таблицу на длительное время.
Есть исключения. Старые версии MySQL могут блокировать таблицу во время ALTER TABLE. Добавление колонки в определенную позицию с помощью AFTER также может вызвать больше блокировок, чем добавление в конец. Но в целом добавление nullable колонки — одна из самых безопасных операций, которые можно выполнить на живой базе данных.
Это означает, что вам не нужно окно обслуживания. Вам не нужно ждать часов с низким трафиком. Вы можете выполнить миграцию днем, проверить, что она работает, и перейти к следующему шагу развертывания.
Как это обеспечивает постепенное развертывание
Аддитивные изменения открывают стратегию развертывания, которую трудно реализовать с другими типами изменений схемы: rolling-обновления без координации.
Представьте, у вас десять экземпляров приложения за балансировщиком нагрузки. Вы хотите развернуть новую версию, которая читает и пишет колонку phone. Вот как выглядит процесс:
- Выполните миграцию для добавления колонки
phone. Теперь в схеме есть новая колонка, но все десять экземпляров все еще работают на старом коде, который ее игнорирует. - Обновите один экземпляр до нового кода. Этот экземпляр начинает читать и писать колонку
phone. Остальные девять продолжают работать нормально, потому что никогда не трогают новую колонку. - Постепенно обновляйте остальные экземпляры один за другим. В любой момент этого процесса старые и новые экземпляры сосуществуют без конфликтов.
Это возможно только потому, что изменение схемы аддитивно. Если бы изменение требовало удаления или переименования колонки, старые экземпляры сломались бы в момент выполнения миграции. Вам пришлось бы обновлять все экземпляры сразу, что увеличивает риск и требует тщательной координации.
Когда выходить за рамки аддитивных изменений
Аддитивные изменения безопасны, но не всегда достаточны. Рано или поздно вам понадобится удалить неиспользуемые колонки, изменить типы данных для решения проблем производительности или реструктурировать таблицы для поддержки новых функций. Эти изменения несут больше риска и требуют других стратегий.
Практическая последовательность: начинайте с аддитивных изменений для добавления новых колонок и таблиц, дайте коду приложения догнать изменения, затем планируйте более инвазивные изменения в отдельных миграциях. Каждая миграция должна делать одно и только одно. Не смешивайте аддитивное изменение с деструктивным в одном скрипте миграции.
Краткий чек-лист для аддитивных изменений
Перед выполнением аддитивного изменения в продакшне проверьте следующее:
- Новая колонка nullable или имеет значение по умолчанию?
- Миграция только добавляет, а не изменяет или удаляет существующую схему?
- Вы протестировали миграцию на копии продакшн-данных?
- У вас есть план отката? (Для аддитивных изменений откат обычно — это просто
ALTER TABLE DROP COLUMN, но проверьте, что это работает.) - Старый код приложения может продолжать работать без изменений после миграции?
Если на все вопросы вы ответили «да», вы готовы выполнить миграцию с уверенностью.
Конкретный вывод
Аддитивные изменения — самая безопасная категория модификаций схемы базы данных, потому что они расширяют схему, не ломая ничего, от чего зависит старый код. Используйте nullable колонки или значения по умолчанию, выполняйте их в обычное рабочее время и развертывайте экземпляры приложения постепенно. Этот подход снимает напряжение между развитием базы данных и поддержанием стабильности продакшна. Начинайте каждое изменение схемы с вопроса: могу ли я сделать его аддитивным? Если да, делайте это сначала. Более рискованные изменения оставьте на потом, когда новая схема уже используется, а старый код выведен из эксплуатации.