Как устроены миграции баз данных и какие проблемы с ними бывают
Что проверяют
Понимаете ли вы, что миграции это версионирование схемы, и знаете ли, чем опасно их применение на живой базе.
Как отвечать
Миграция это скрипт изменения схемы, лежащий в репозитории и применяемый инструментом, который ведёт учёт применённого в служебной таблице. В Go это goose, golang-migrate, atlas.
Базовые правила: только вперёд, применённый файл не переписывается, нумерация сплошная, применение отдельным шагом выкладки до запуска нового кода.
Проблемы, ради которых вопрос и задают:
- блокировки:
ALTER TABLEберёт блокировку на таблицу. Добавление колонки без значения по умолчанию мгновенно (меняются только метаданные), а вот сDEFAULTна старых версиях PostgreSQL переписывалась вся таблица. Индекс строят черезCREATE INDEX CONCURRENTLY, иначе запись встаёт; - несовместимость со старым кодом: во время выкладки одновременно работают обе версии приложения. Удаление или переименование колонки ломает старую. Отсюда схема в три шага: добавить новое и писать в оба места, перенести данные, убрать старое в следующем релизе;
- долгие бэкофиллы: один
UPDATEна миллионы строк держит транзакцию и раздувает таблицу. Делают пачками с паузами; - откат: обратная миграция часто невозможна без потери данных. Практичнее не откатывать, а выпускать исправляющую миграцию;
- расхождение окружений: применили руками на одном стенде и забыли записать, дальше история не сходится;
- параллельное применение: два экземпляра приложения стартуют одновременно, поэтому инструмент должен брать блокировку.
Где спотыкаются
Правят уже применённую миграцию.
Строят индекс обычной командой на большой боевой таблице.
Делают несовместимое изменение одним шагом и роняют выкладку.
Запускают миграции из кода приложения при старте, получая гонку между репликами.