Как стать автором
Обновить
207.26
Postgres Professional
Разработчик СУБД Postgres Pro

Миграция Левиафана: как удалось перевести базу в 40 ТБ данных на Postgres Pro

Уровень сложностиПростой
Время на прочтение6 мин
Количество просмотров4.7K

Представьте задачу: взять рабочий высоконагруженный государственный сервис с огромной БД под 50 терабайт, тесно интегрированной с бизнес-процессами и используемой тысячами пользователей, и практически безболезненно перенести его с Oracle на Postgres Pro. Те, кто знает цену таких решений, сразу предполагают масштаб бедствия: простои, падения производительности, потерянные данные… Но всё же миграция состоялась, и мы с ОТР расскажем как.

Проект импортозамещения, в рамках которого проводились работы, стартовал в 2024 году и включал миграцию трёх систем разного профиля. К ноябрю настал черёд самой сложной: высоконагруженной, с огромной базой данных и высокой частотой обновлений.

Оптимизация данных от 52 ТБ к 40 ТБ и стратегии этапов

Изначально база данных Oracle весила внушительные 52 ТБ. Первым делом провели инвентаризацию. «В топе у нас вылезли логи, исторические данные», — рассказывает Ирина Токарева. По согласованию с заказчиком, эти данные решили не переносить в Postgres Pro, а оставить в Oracle, переместив их после миграции на более медленные диски и другие серверы. Таким образом, к началу миграции подошли с объёмом в 40 ТБ.

Тестовая миграция этих 40 ТБ показала, что полный перенос займёт 5 дней — непозволительная роскошь для критически важной системы. Стало очевидно: единственный путь — поэтапная миграция.

Стратегия выглядела так:

  1. Миграция этапами.

  2. Инструмент для «большого» переноса — стандартная утилита ora2pg.

  3. Синхронизация изменений (diff). Между этапами накатывается разница данных, наработанная пользователями в промышленной среде (PROD).

  4. Технологическое окно. Каждый этап укладывался в окно с 20:00 пятницы до 02:00 понедельника.

  5. Обработка на стороне Postgres Pro. Чтобы минимизировать влияние на PROD Oracle, вся логика обработки diff'а должна была выполняться на стороне Postgres Pro.

Изначально планировали два этапа, но жизнь внесла коррективы. Условием успешной миграции считалось снятие полного бэкапа и создание реплики. Так, два этапа превратились в три:

  • 1-й и 2-й этапы — перенос самых больших таблиц, их индексов и констрейнтов (суммарно 30 ТБ);

  • 3-й этап — всё остальное (10 ТБ).

Бэкап и создание реплики решили не откладывать на самый конец, а сделали между вторым и третьим этапами. Бэкап занял почти сутки, подтвердив правильность этого решения. В дальнейшем для синхронизации бэкапа и реплики использовали только WAL-логи.

Эволюция обработчика Diff

Ключевым элементом миграции стал механизм накатки изменений (diff), который претерпел четыре итерации.

Общая схема:

  • Oracle. На исходных таблицах (Source) создавался триггер на операции INSERT, UPDATE, DELETE.

  • Change Tracking Table (Oracle). Триггер записывал изменения в специальную таблицу отслеживания, состоящую из трёх полей: ID (PK самой трекинговой таблицы), KEY (PK исходной таблицы), EVENT (тип операции: I, U, D).

  • Postgres Pro. Исходная таблица Oracle и трекинговая таблица были доступны в Postgres Pro как Foreign Tables (через FDW).

  • Обработчик (Postgres Pro). Самописная процедура на стороне Postgres Pro, обёрнутая в джоб с использованием pgpro_scheduler.

Модель 1: прямолинейный подход

Обработчик считывал цепочку изменений из трекинговой таблицы, получал по этим ключам актуальные данные из Source-таблицы Oracle и записывал их в Target-таблицу Postgres Pro. После этого обработанные записи удалялись из трекинговой таблицы.

Проблема: почти 70% транзакций завершались ошибкой ORA-08177: can’t serialize access for this transaction при попытке удалить записи из трекинговой таблицы Oracle со стороны Postgres. На тот момент решение не нашли.

Модель 2: отказ от очистки и «потерянные» данные

Решили не удалять записи из трекинговой таблицы в рамках основной транзакции. Вместо этого ввели таблицу-плейсхолдер (sync_left_position) на стороне Postgres Pro, куда записывался ID последней обработанной записи из трекинговой таблицы.

Проблема: количественные расхождения данных. Причины:

  • очень широкий кеш последовательности (sequence cache) на стороне Oracle для ID трекинговой таблицы (до 12 000);

  • специфика приложения — оно могло «захватить» ID из последовательности, но зафиксировать транзакцию позже.

В результате обработчик считывал диапазон ID (например, с 1 по 1010), а позже в трекинговой таблице появлялись записи с ID 1008, 1009 с операциями DELETE/UPDATE, которые уже «проскочили» мимо обработчика.

Модель 3: логирование и асинхронная очистка

Усложнили схему:

  • все обработанные записи (ID из трекинговой таблицы и PK исходной таблицы) логировались в отдельную таблицу на стороне Postgres Pro;

  • второй обработчик (Feedback) работал автономно и асинхронно. Он читал лог, брал порцию ключей и удалял соответствующие записи из трекинговой таблицы Oracle и из самого лога.

Проблема: скорости всё ещё не хватало для таблиц с интенсивным изменением (до 100 миллионов изменений в сутки). «Даже если мы в пятницу останавливали триггер, у нас генерился такой дифф, который мы не успевали разгрести за два выходных», — вспоминает Ирина.

Модель 4: вводим параллелизм

Единственным выходом стало распараллеливание.

Каждый процесс обработки получил свой плейсхолдер для отслеживания позиции. В процедуру добавили параметры: P_Divider (число параллельных процессов) и P_Piece (обрабатываемый «кусок» данных, определяемый по остатку от деления ключа).

Автоматизировали создание джобов в pgpro_scheduler для запуска параллельных обработчиков.

Масштабирование удалось. Подготовили скрипты для горизонтального масштабирования.

Проблема: обработчики стали оказывать негативное влияние на PROD Oracle. Пользователи начали жаловаться.

Для решения проблем ограничили работы по накатке diff'а окном низкой нагрузки (с 16:00 до 02:00 ночи). Для самых «горячих» таблиц параллельность довели до 64 потоков.

Что можно было сделать иначе?

Уже находясь на третьей модели, команда нашла решение проблемы ORA-08177 из первой модели. Ошибка лечилась простой настройкой Foreign Data Wrapper: ALTER SERVER oracle_fdw OPTIONS (SET isolation_level 'read_committed'); (по умолчанию serializable). Это позволило бы остаться на первой, более простой модели.

Также была заготовка для пакетной обработки (batching) — DELETE ... WHERE key = ANY($1) и INSERT ... SELECT ... WHERE key = ANY($1). Это могло бы ускорить процесс, но имело ограничение со стороны Oracle: конструкция IN поддерживала не более 1000 записей, тогда как в PROD обрабатывались пачки по 100 000. Однако от распараллеливания уйти бы всё равно не удалось.

Сюрпризы от NUMA

Миграция завершилась успешно, но «приключения» только начинались. Стабилизация системы заняла около недели.

1. NUMA-архитектура и производительность

В понедельник утром после миграции пришлось срочно менять сервер под мастер-базу с 16-сокетного на 8-сокетный (оба с NUMA-архитектурой). PostgreSQL не очень хорошо работает с NUMA, и на 16-сокетной машине это усугублялось, приводя к «совершенно драматической производительности». Переезд на 8-сокетный сервер резко улучшил ситуацию, так как взаимодействие между NUMA-нодами происходило в рамках одной материнской платы, а не через отдельную шину, как на 16-сокетном.

Проблемы с NUMA проявлялись и на этапе миграции (низкая скорость переливки). Тогда подключилась команда Postgres Professional и по рекомендации для стабилизации работы остались на двух NUMA-нодах, что помогло. Перед выходом в PROD задействовали все NUMA-ноды. Нагрузочное тестирование не выявило всех проблем, которые «выстрелили» в реальной эксплуатации.

Отставание реплики также было связано с NUMA на 16-сокетном сервере реплики. Ликвидировали отставание, поменяв сервер реплики на 8-сокетный.

2. Foreign Keys (FK)

Запросы с проверкой FK стали одними из самых тяжёлых. Проверка FK (SELECT 1 FROM X WHERE id = Y FOR NO KEY UPDATE) блокировала строки и при большом количестве параллельных транзакций генерировала мультитранзакции, включающие все активные ID транзакций, что создавало огромный оверхед.

Опять же по рекомендации Postgres Professional, на особо популярных таблицах от FK пришлось отказаться. Позже выяснилось, что это поведение является багом, который был отдан в доработку.

3. HOT Update и счётчики

Проблема возникла с таблицей, используемой как счётчик для генерации номеров платёжных поручений. При интенсивном обновлении одной и той же строки (до 300–500 раз в секунду) узким местом стал HOT Update. Внутренняя очистка генерировала очень длинные цепочки «мёртвых» строк.

С помощью команды Postgres Professional решили создать индекс на одно из изменяемых полей. Это помогло: мы отключили внутристраничную очистку, так как одним из условий её работы является как раз отсутствие индексов на обновляемых полях.

4. Внутренний мониторинг

На период стабилизации отключили мониторинг pgpro_stats_statements в пользу ванильного pg_stats_statements из-за очень больших накладных расходов.

Выводы и уроки

Несмотря на сложности перехода, миграция завершилась успешно. После двух недель стабилизации производительность Postgres Pro не уступала Oracle по значимым метрикам, а заказчик остался доволен результатами.

 15 ноября.2024 – последний день работы пользователей в oracle. В самый «горячий период» c 09:00 до 10:00 – 1998,8 tps (transactions per second)
15 ноября.2024 – последний день работы пользователей в oracle. В самый «горячий период» c 09:00 до 10:00 – 1998,8 tps (transactions per second)
 26 ноября 2024 09:00-10:00 – вторая неделя работы пользователей в postgres: 2628 tps
26 ноября 2024 09:00-10:00 – вторая неделя работы пользователей в postgres: 2628 tps
 Минимальное и максимальное время выполнения запросов 2964.39 и 5251.27, а количество обработанных запросов - 42834.
Минимальное и максимальное время выполнения запросов 2964.39 и 5251.27, а количество обработанных запросов - 42834.
 2 декабря 2024 09:00-10:00 – третья неделя работы пользователей в postgres: 3760 tps.
2 декабря 2024 09:00-10:00 – третья неделя работы пользователей в postgres: 3760 tps.
 Минимальной и максимальное время выполнения запросов снизилось, а количество обработанных запросов поднялось до 70976.
Минимальной и максимальное время выполнения запросов снизилось, а количество обработанных запросов поднялось до 70976.

Ретроспектива заставила команду по-другому взглянуть на многие моменты:

  • простой настройкой FDW (isolation_level='read_committed') можно было избежать сложных перепроектирований системы;

  • важно заранее освобождать проект от архивных и исторических данных;

  • нагрузочное тестирование должно быть максимально полным и учитывать работу под интенсивными нагрузками;

  • время на миграцию всегда желательно закладывать с запасом, так как даже тщательно подготовленный проект неожиданно может столкнуться с неявными проблемами.

Созданный инструмент миграции и подходы накатки diff оказались универсальными и могут переиспользоваться для других проектов аналогичного масштаба и сложности.

Теги:
Хабы:
+27
Комментарии21

Публикации

Информация

Сайт
www.postgrespro.ru
Дата регистрации
Дата основания
Численность
201–500 человек
Местоположение
Россия
Представитель
Иван Панченко