Обновление мажорной версии базы данных на проде
Мажорное обновление базы данных — это не «накатить пакет и перезапустить сервис», как с патч-версией. Между PostgreSQL 14 и 17 или между MySQL 5.7 и 8.0 меняется поведение по умолчанию, исчезают функции, на которые опирается ваш код, и иногда меняется формат хранения на диске. Если подойти к этому как к рутинному апдейту, есть шанс уронить прод в рабочий понедельник и потратить ночь на восстановление. Ниже — методичный подход, которым можно закрыть большую часть рисков заранее, а не разгребать их постфактум.
Содержание
- Почему мажорное обновление — не то же самое, что патч
- Шаг 1. Обязательное чтение release notes — что именно искать
- Шаг 2. Тестовое окружение на копии реальных данных
- Шаг 3. Полный бэкап перед стартом работ на проде
- Шаг 4. Выбор метода обновления в зависимости от масштаба
- Шаг 5. План отката — что делать, если что-то пошло не так
- Окно обслуживания, коммуникация и время апгрейда
Почему мажорное обновление — не то же самое, что патч
Патч-версия (14.9 → 14.10) почти всегда безопасна: это исправления багов и уязвимостей без изменения совместимости — можно обновлять в рамках регулярного окна обслуживания без глубокого анализа. Мажорная версия (14 → 17) — другая история:
- Меняется формат данных на диске. В PostgreSQL это значит, что нельзя просто заменить бинарники и запустить сервер на старых файлах — нужен
pg_upgrade, логическая репликация или дамп/restore. - Меняется поведение по умолчанию. Пример: PostgreSQL 15 изменил права на схему
public— по умолчаниюCREATEв ней больше не разрешён всем ролям. Если ваши миграции или ORM полагались на старое поведение, они начнут падать сразу после апгрейда. - Удаляются устаревшие функции и синтаксис. MySQL 8.0 убрал query cache, изменил обработку
GROUP BYбез агрегатных функций (строгийONLY_FULL_GROUP_BYпо умолчанию), сменил метод аутентификации по умолчанию наcaching_sha2_password— старые клиентские библиотеки могут не уметь его использовать без правки конфига. - Меняется поведение расширений. PostGIS, pg_stat_statements, pgvector и другие расширения имеют свою матрицу совместимости с мажорными версиями СУБД — обновление ядра без синхронного обновления расширений может привести к отказу при старте.
Отсюда вывод: мажорное обновление требует отдельного плана, а не строчки в чеклисте регулярного техобслуживания.
Шаг 1. Обязательное чтение release notes — что именно искать
Прежде чем трогать что-либо на проде, откройте официальные release notes новой версии и целенаправленно найдите раздел о несовместимостях (в PostgreSQL это обычно называется «Migration to Version N» и идёт первым в списке изменений релиза, в MySQL — «Changes Affecting Upgrades» в Upgrade Notes). Читайте не по диагонали, а по конкретным пунктам:
- Изменения поведения по умолчанию. Запросы, которые раньше молча работали, теперь либо ошибаются, либо ведут себя иначе без явной ошибки — это самое опасное, молчаливая смена семантики.
- Удалённые или устаревшие (deprecated) функции. Если функция помечена deprecated ещё в предыдущей мажорной версии, велика вероятность, что в этой её уже удалили.
- Изменения синтаксиса SQL и параметров конфигурации. Зарезервированные слова, новые обязательные приведения типов, переименованные или удалённые GUC-параметры (PostgreSQL) / system variables (MySQL) — если такой параметр остался в
postgresql.confилиmy.cnf, сервис может не стартовать вовсе. - Протокол репликации и инструменты бэкапа. Если используете pgBackRest или похожий инструмент — проверьте его собственную матрицу поддерживаемых версий СУБД.
- Совместимость расширений и коннекторов. Версии PostGIS/pgvector под новую версию ядра, версии драйверов (psycopg2, mysqlclient, JDBC) — иногда клиентская библиотека тоже требует обновления.
Практический приём: выпишите из release notes конкретный список пунктов, которые потенциально касаются именно вашей схемы и запросов (а не просто скопируйте весь список изменений). Это и станет чек-листом для тестового прогона на следующем шаге.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверШаг 2. Тестовое окружение на копии реальных данных
Это тот шаг, который чаще всего пропускают — и совершенно зря, потому что именно на нём всплывает 90% проблем. Идея простая: не обновлять прод, чтобы «посмотреть, что будет», а сначала посмотреть на копии.
- Разверните отдельный сервер с той же ОС и конфигурацией, что и прод (не обязательно тот же объём ресурсов — важна софтверная идентичность, а не мощность).
- Перенесите на него копию продакшн-данных. Дамп через
pg_dump/mysqldumpдля небольших баз, физическая копия (снапшот диска,pg_basebackup) для крупных — чем ближе к реальным данным по объёму и разнообразию, тем достовернее тест. Если в данных есть персональные данные клиентов и это критично по требованиям безопасности — маскируйте чувствительные поля перед прогоном на тестовом стенде. - Установите новую мажорную версию рядом и прогоните проверку совместимости заранее, до фактического апгрейда:
pg_upgrade --checkдля PostgreSQL сверяет кластеры и сообщает о несовместимостях (например, устаревшие типы данных,reg*-столбцы, требующие ручного вмешательства), не трогая данные. - Прогоните реальные запросы приложения, а не абстрактный
SELECT 1. Возьмите топ медленных и топ частых запросов изpg_stat_statements(если он у вас включён) или из slow query log MySQL и выполните их на новой версии — сравните план выполнения (EXPLAIN ANALYZE) и результат. Ещё лучше — прогнать staging-копию приложения целиком против тестовой базы: запустить интеграционные тесты, ключевые пользовательские сценарии, фоновые джобы и миграции. - Проверьте логи на предупреждения, а не только на явные ошибки — многие несовместимости выражаются в виде warning, который легко пропустить, если смотреть только на exit code.
Если на этом этапе что-то ломается — это отличная новость: у вас есть время всё исправить (доработать код, поправить конфиг, обновить расширение) без давления «прод уже недоступен». Подробнее про сам процесс переноса данных между серверами для тестового стенда можно посмотреть в статье про перенос большой базы с минимальным даунтаймом — те же приёмы годятся и для разворачивания тестовой копии.
Шаг 3. Полный бэкап перед стартом работ на проде
Даже если тестовый прогон прошёл идеально — это не гарантия, что на реальном проде всё пойдёт так же. Тестовые данные почти никогда не воспроизводят на 100% распределение реальных данных, редкие edge-case записи, специфичные для продакшена индексы или объём одновременных подключений. Поэтому правило простое и не обсуждается: полный бэкап перед началом работ, независимо от того, насколько гладко прошло тестирование.
Что именно нужно сделать перед стартом:
- Снять физический бэкап всего кластера (не только логический дамп) — это даёт возможность восстановить состояние «как было» максимально быстро, без пересборки индексов из дампа.
- Обязательно проверить, что бэкап реально восстанавливается — бэкап, который никто не пробовал развернуть, для целей отката не считается существующим.
- Зафиксировать точку восстановления (например, отметить LSN или время бэкапа), чтобы при необходимости точечного отката было от чего оттолкнуться.
- Убедиться, что место на диске бэкап-хранилища действительно есть — банальная, но частая причина провала аварийного бэкапа в последний момент.
Как организовать сам процесс регулярного и аварийного резервного копирования баз данных подробно разобрано в статье про частые ошибки при резервном копировании БД на сервере, а практику восстановления из бэкапа — в статье восстановление базы данных из бэкапа на практике. Прогоните восстановление один раз заранее, до дня апгрейда — тестировать процедуру отката в момент, когда она реально нужна, уже поздно.
Шаг 4. Выбор метода обновления в зависимости от масштаба
Метод обновления определяется в первую очередь объёмом данных и допустимым временем простоя, а не личными предпочтениями.
Для небольшой и средней базы (условно — до нескольких десятков гигабайт, простой в разумных пределах) подходит обновление на месте через pg_upgrade:
# Проверка совместимости без изменения данных
/usr/lib/postgresql/17/bin/pg_upgrade \
--old-bindir=/usr/lib/postgresql/14/bin \
--new-bindir=/usr/lib/postgresql/17/bin \
--old-datadir=/var/lib/postgresql/14/main \
--new-datadir=/var/lib/postgresql/17/main \
--check
# Сам апгрейд, оба кластера должны быть остановлены
/usr/lib/postgresql/17/bin/pg_upgrade \
--old-bindir=/usr/lib/postgresql/14/bin \
--new-bindir=/usr/lib/postgresql/17/bin \
--old-datadir=/var/lib/postgresql/14/main \
--new-datadir=/var/lib/postgresql/17/main \
--link
Режим --link использует жёсткие ссылки вместо копирования файлов и занимает минуты даже на базе в десятки гигабайт — это его главное преимущество. Но у него есть важный нюанс, о котором нередко забывают (см. следующий раздел про откат). После апгрейда обязательно прогоните vacuumdb --all --analyze-in-stages — без свежей статистики планировщик первое время будет строить неоптимальные планы запросов, и вы получите не сбой, а внезапную деградацию производительности сразу после релиза.
Для очень большой базы, где простой даже на 20-30 минут ощутим для бизнеса, разумнее развернуть новую версию параллельно и перенести данные с минимизацией простоя:
- Поднять новый сервер с уже установленной новой мажорной версией СУБД.
- Настроить логическую репликацию (нативная логическая репликация PostgreSQL, доступная начиная с версии 10, умеет реплицировать между разными мажорными версиями — это ровно наш случай) с продакшн-сервера на новый.
- Дать реплике догнать прод по данным, проверить консистентность на выборочных таблицах.
- В момент переключения — короткое окно: остановить запись на старом сервере, дождаться, пока реплика догонит последние транзакции, переключить приложение на новый сервер.
Таблица для ориентира, какой метод когда выбирать:
| Метод | Простой | Когда подходит | Сложность отката |
|---|---|---|---|
pg_upgrade --link | Минуты | Небольшая/средняя база, простой в это окно допустим | Низкая, но только через отдельный бэкап (см. ниже) |
pg_upgrade --copy | Дольше (копирует файлы) | Когда важно сохранить старый кластер нетронутым | Высокая — старый кластер сразу доступен для отката |
Дамп/restore (pg_dump/pg_dumpall) | От часов на больших базах | Очень маленькие базы или смена архитектуры хранения | Высокая — старая база не тронута |
| Логическая репликация + cutover | Секунды-минуты на переключение | Крупная база, критичен даунтайм | Очень высокая — старый сервер можно оставить в резерве |
Практика переноса больших объёмов данных с минимальным простоем детально разобрана в статье про перенос большой базы с минимальным даунтаймом — там же про настройку самой логической репликации можно опереться на материал по репликации PostgreSQL на сервере.
Шаг 5. План отката — что делать, если что-то пошло не так
Явный план отката должен быть написан и согласован с командой ДО начала работ, а не придумываться в панике посреди инцидента. Здесь есть важная деталь, которую многие упускают.
Грабля с pg_upgrade --link: после запуска нового кластера в режиме link старый кластер официально считается небезопасным для повторного запуска. Жёсткие ссылки означают, что часть файлов физически общая между старым и новым datadir — как только новая версия начинает писать (а автовакуум запускается почти сразу после старта), это может испортить данные и в «старой» копии тоже. Официальная документация PostgreSQL прямо предупреждает: не рассчитывайте на то, что после --link-апгрейда можно будет просто перезапустить старый сервер как ни в чём не бывало. Практический вывод: реальный план отката после --link-апгрейда — это восстановление из отдельного бэкапа, снятого на шаге 3, а не запуск старого datadir. Учитывайте это во времени простоя: восстановление из бэкапа обычно дольше, чем сам откат путём «просто перезапустить старую версию».
Из этого вытекает структура плана отката:
- Если использовали
pg_upgrade --copyили дамп/restore — старый кластер физически не тронут, откат — это просто снова направить приложение на старый сервер/datadir и запустить его. - Если использовали
pg_upgrade --link— план отката = восстановление из бэкапа, снятого перед началом работ. Заранее прикиньте, сколько это займёт по времени на вашем объёме данных, и заложите это время в окно. - Если использовали параллельный сервер с логической репликацией — это самый безопасный сценарий с точки зрения отката: старый сервер физически не трогали, он либо ещё принимает запись, либо остановлен, но цел. Держите его в режиме ожидания (не удаляйте и не переиспользуйте под другие задачи) какое-то время после переключения — от нескольких часов до нескольких дней, в зависимости от критичности сервиса — именно на случай, если проблема обнаружится не в первые пять минут, а спустя время под реальной нагрузкой.
- Определите заранее критерии «откатываемся»: конкретный рост процента ошибок в логах приложения, конкретная деградация времени ответа, конкретные алерты мониторинга. Без заранее прописанных критериев решение об откате принимается на эмоциях и обычно с опозданием.
Окно обслуживания, коммуникация и время апгрейда
Технически правильный план можно испортить неудачным выбором момента и отсутствием коммуникации.
- Выбирайте окно с минимальной нагрузкой — по логам трафика и активности пользователей, а не «на глаз». Ночь для одной аудитории может быть худшим временем для другой (например, если основная аудитория в другом часовом поясе).
- Заранее сообщите команде и, если уместно, пользователям о плановом окне и возможном коротком простое. Это не формальность: если что-то пойдёт не по плану, команда поддержки не должна тратить первые критичные минуты на выяснение, что вообще происходит.
- Убедитесь, что на время окна и следующие несколько часов после него доступны нужные люди — не только тот, кто нажимает кнопку апгрейда, но и тот, кто знает приложение и сможет быстро отличить «баг из-за апгрейда БД» от «случайно совпало».
- Не проводите такое обновление в пятницу вечером. Это классическая рекомендация не просто ради шутки: если проблема проявится не сразу, а спустя несколько часов под ночной или выходной нагрузкой, у вас будет меньше людей на связи и меньше времени на спокойную реакцию до следующего рабочего дня. Понедельник-четверг, первая половина дня по вашему рабочему времени — обычно разумный выбор.
Честно: даже при соблюдении всех шагов есть остаточный риск — production почти никогда не воспроизводится тестовым стендом на 100%. Цель методичного подхода не в том, чтобы свести риск к нулю, а в том, чтобы перевести неизвестные проблемы в известные заранее, а оставшиеся неизвестные встретить с готовым и проверенным планом отката.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Сколько реально занимает pg_upgrade в режиме --link на боевой базе?
Сама операция обычно занимает минуты даже на базах в десятки-сотни гигабайт, потому что данные не копируются. Но это не всё время простоя: после апгрейда нужно прогнать ANALYZE для сбора статистики — без этого первые запросы после апгрейда могут выполняться заметно медленнее из-за неоптимальных планов, и это стоит закладывать в окно отдельно.
Нужно ли останавливать приложение на время pg_upgrade?
Да, оба кластера — старый и новый — должны быть полностью остановлены на время выполнения pg_upgrade. Это принципиальное отличие от подхода с логической репликацией, где простой сводится к короткому окну самого переключения.
pg_upgrade --check нашёл несовместимости — что делать?
Не запускать апгрейд, пока проверка не пройдёт чисто. Обычно --check указывает конкретные объекты (например, столбцы определённых устаревших типов) — их нужно исправить на старой версии заранее, до попытки апгрейда, и повторить проверку.
Можно ли настроить логическую репликацию между сильно разными мажорными версиями, например PostgreSQL 12 и 17?
В целом да — нативная логическая репликация не требует одинаковой мажорной версии на подписчике и издателе. Но чем больше разрыв версий, тем внимательнее стоит проверить совместимость типов данных и используемых расширений на тестовом стенде — не полагайтесь только на то, что подключение установилось и репликация «пошла».
Нужно ли что-то делать с расширениями типа PostGIS или pg_cron после апгрейда ядра?
Да — после обновления версии СУБД для каждого установленного расширения обычно нужно выполнить ALTER EXTENSION ... UPDATE, а перед началом работ проверить, что версия самого расширения вообще поддерживает новую мажорную версию СУБД — это отдельный пункт release notes расширения, а не только ядра базы.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →