У нас есть бэкенд-приложение, которое использует PostgreSQL. Как бы вы обновили схему таблицы PostgreSQL без даунтайма приложения? С учетом того, что миграция содержит ломающие изменения.
Объясните шаги по модификации структуры таблицы, гарантируя, что ваше приложение продолжает работать во время процесса миграции.
Подсказки:
- Учитывайте обратную совместимость при модификации столбцов.
- Подумайте об подходах, таких как использование временных таблиц или нескольких развертываний.
- Учитывайте как операции чтения, так и записи во время миграции.
Выше ожиданий:
- Понимание стратегии двойных записей и фича-флагов (feature flags) для фаз миграции
- Знание расширения pg_repack
- Знание соображений по уровням изоляции транзакций и шаблонов по backfilling данных для больших таблиц
При модификации схем баз данных в пром среде основной целью является поддержание доступности сервиса при миграции схемы данных. Миграции без простоев требуют тщательного планирования.
Базовая стратегия для миграций схем без даунтайма приложения
- Убедитесь, что все узлы кластера доступны и находятся в нормальном режиме (не в режиме обслуживания)
- Работающее приложение должно поддерживать две версии схемы данных, до ломающих изменений и после.
- Добавление перед удалением — всегда выполняйте добавление изменений перед деструктивными операциями
- Разверните изменения в несколько шагов
- Убедитесь, что все изменения схемы обратно совместимы и можно откатиться к состоянию до начала миграции
- Координируйте релизы и приложений, и базы для поддержания порядка.
Пошаговый подход
Фаза 1: Подготовка приложения
- Обновите код приложения, чтобы обработать как старые, так и новые версии схемы
- Реализуйте фича-тоглы, которые позволяют переключаться между схемами
- Разверните обновленное приложение, которое может работать с обеими схемами
- Проверьте корректность работы развертывания
- Сделайте изменения первой фазы
-- Не делайте этого в проме (приводит к блокировкам):
ALTER TABLE users DROP COLUMN address;
-- Вместо этого сначала добавьте новые столбцы/таблицы:
ALTER TABLE users ADD COLUMN street VARCHAR(255);
ALTER TABLE users ADD COLUMN city VARCHAR(255);
ALTER TABLE users ADD COLUMN zip_code VARCHAR(10);
Фаза 2: Миграция базы данных
Для простых изменений (добавление nullable столбцов, создание индексов):
-- Добавить nullable столбцы (быстро, без блокировок)
ALTER TABLE users ADD COLUMN email VARCHAR(255);
-- Создать индексы одновременно (избегает блокировок записи)
CREATE INDEX CONCURRENTLY idx_users_email ON users(email);
Для более сложных изменений:
-- Переименовать столбцы
ALTER TABLE users RENAME COLUMN user_name TO username;
-- Изменить тип столбца (потенциально блокирующая операция)
-- Используйте промежуточный столбец вместо прямого ALTER COLUMN
ALTER TABLE products ADD COLUMN price_new DECIMAL(10,2);
-- Затем выполнить обратную загрузку данных порциями (см. ниже)
Фаза 3: Обратная загрузка данных (backfilling)
Для больших таблиц загружайте данные небольшими порциями, чтобы избежать блокировок:
-- Обратная загрузка порциями по 1000 строк
DO $$
DECLARE
batch_size INT := 1000;
max_id INT;
current_id INT := 0;
BEGIN
SELECT MAX(id) INTO max_id FROM products;
WHILE current_id < max_id LOOP
UPDATE products
SET price_new = price::DECIMAL(10,2)
WHERE id > current_id AND id <= current_id + batch_size;
current_id := current_id + batch_size;
COMMIT;
PERFORM pg_sleep(0.1); -- Уменьшение нагрузки
END LOOP;
END $$;
Фаза 4: Переход к новой схеме
- Разверните приложение, чтобы оно использовало только новую схему
- Проверьте корректность работы всего
- Удалите старые части схемы
-- После успешной миграции
ALTER TABLE products DROP COLUMN price;
ALTER TABLE products RENAME COLUMN price_new TO price;
Продвинутые техники
Использование pg_repack
pg_repack
позволяет удалять мусор из таблиц и индексов, а также, по желанию, восстанавливать физический порядок кластеризованных индексов.
Для операций, которые обычно блокируют таблицу:
pg_repack -h localhost -d mydb -t products --no-order
Схема двойной записи
- Записывать данные в обе схему, старую и новую структуры во время перехода
- Реализовать проверки согласованности между старыми и новыми данными
- Постепенно переводить чтение на новую структуру
- После уверенности удалить старую структуру
Уровни изоляции транзакций
- Используйте уровень изоляции
READ COMMITTED
во время миграций - Для критических операций рассмотрите
SERIALIZABLE
, но будьте внимательны к влиянию на производительность
Мониторинг и безопасность
- Тщательно протестируйте миграции в тестовых средах и на staging
- Мониторьте очереди блокировок во время миграции с помощью:
SELECT relation::regclass, mode, granted, pid, age(clock_timestamp(), query_start)
FROM pg_locks l JOIN pg_stat_activity a ON l.pid = a.pid
WHERE relation = 'users'::regclass AND NOT granted;
- Имейте план отката для каждого шага
- Рассмотрите возможность реализации канареечных деплоев для изменений схемы
Следуя таким шагам, вы можете изменять схемы PostgreSQL с минимальным влиянием на доступность приложения, обеспечивая плавный переход между версиями базы данных.