Как стать автором
Обновить
4
Карма
0
Рейтинг
Василий @Silron

Пользователь

  • Подписчики
  • Подписки

Феншуйная автоматизация CI & CD с помощью Jenkins и Jira

Используем jira-rest-java-client и Jira без кастомных плагинов.
Функциональности пока хватает.

Экстремальная миграция на PostgreSQL: без остановки, потерь и тестирования

Для основной массы таблиц использовался более простой, надежный и дешевый способ миграции через SymmetricDS. В конце проекта «легкие» таблицы кончились и последние 1.5 месяца мы потратили на ручную миграцию через специально написанные очереди миграции данных. Такой подход требует больше ресурсов на разработку и внимательного тестирования.
По аналогии с SymmetricDS, очередь выполняла не только синхронизацию новых и измененных данных, но и начальную массовую загрузку исторических данных в батч режиме.

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

Экстремальная миграция на PostgreSQL: без остановки, потерь и тестирования

Поясню с чем мы столкнулись на примере.

На Oracle запрос выглядел вот так:
SELECT EVENT_ID, EVENT_TYPE 
FROM EVENT
WHERE EVENT_DATE < :eventDate AND ROWNUM = 1 FOR UPDATE SKIP LOCKED

Для Postgres мы переписали его вот так:
SELECT EVENT_ID, EVENT_TYPE 
FROM EVENT
WHERE EVENT_DATE < :eventDate FOR UPDATE SKIP LOCKED LIMIT 1

И поняли, что получили нереально большую просадку по производительности.
При этом количество записей, которые попадали под условие EVENT_DATE < :eventDate было небольшим — десятки.

Посмотрели план запроса:
postgres@demo:5432 (demo-service) # explain (analyze,buffers) 
SELECT EVENT_ID, EVENT_TYPE FROM EVENT WHERE EVENT_DATE < now() + '30 day' limit 100;
                                    QUERY PLAN                                                           
-----------------------------------------------------------------------------------------------
Limit  (cost=0.00..59.53 rows=100 width=18) (actual time=0.022..0.817 rows=100 loops=1)
   Buffers: shared hit=20
   ->  Seq Scan on event  (cost=0.00..154329.33 rows=559239 width=18) (actual time=0.022..0.807 rows=100 loops=1)

Индекс по EVENT_DATE есть, но видно, что Postgres его не использует и выполняется Seq Scan.

Лечение заключается в прямом указании Postgres на использование нужного индекса путем добавления сортировки, которая не требуется с точки зрения бизнес-логики клиента:
SELECT EVENT_ID, EVENT_TYPE
FROM EVENT
WHERE EVENT_DATE < $1 order by EVENT_DATE FOR UPDATE SKIP LOCKED limit 1

В итоге получаем Index Scan:
postgres@demo:5432 (demo-service) # explain (analyze,buffers)
SELECT EVENT_ID, EVENT_TYPE FROM EVENT WHERE EVENT_DATE < now() + '30 day' order by EVENT_DATE limit 100;
                                     QUERY PLAN                                                                              
-----------------------------------------------------------------------------------------------
Limit  (cost=0.44..87.03 rows=100 width=26) (actual time=0.061..183.099 rows=100 loops=1)
   Buffers: shared hit=28 read=89
   I/O Timings: read=182.489
   ->  Index Scan using event_idx1 on event  (cost=0.44..205111.55 rows=559239 width=26) (actual time=0.060..183.057 rows=100 loops=1)
         Index Cond: (event_date < (now() + '30 days'::interval))
         Buffers: shared hit=28 read=89
         I/O Timings: read=182.489
Planning time: 0.301 ms
Execution time: 183.175 ms

Экстремальная миграция на PostgreSQL: без остановки, потерь и тестирования

Важно, что меняется не только БД, но и её тип. А значит нужен код, который может одновременно работать с двумя типами БД.
Учитывая что код разрабатывался годами и разными людьми, такая миграция представляет ещё большой риск.
И наша статья про то, как давно живущее приложение аккуратно перенести в сжатый срок на новое окружение с полной миграцией всех данных без прерывания процесса.

Экстремальная миграция на PostgreSQL: без остановки, потерь и тестирования

Как уже отмечалось в комментариях, разные сценарии миграции требуют разных подходов. В статье описана конкретная методика на примере одного типа таблиц, для которого целесообразен был именно описанный вариант.
Разумеется, переключение связанных таблиц происходило одновременно, с учетом явной связи в БД — join и неявной — по коду.
Что касается логики в БД, то мы принципиально против такого подхода в своей системе.

Oracle хороший мощный инструмент. Особенности нашей работы не позволяют использовать все его возможности. Нам важны производительность сервиса, утилизация железа, возможность дешевого масштабирования и применение автоматического тестирования.
Все это зачастую закрывает для нас такие теоретические подходы как нормальная форма, логические связи в бд и даже внешние ключи.

Экстремальная миграция на PostgreSQL: без остановки, потерь и тестирования

Все верно — сервис очень живой: постоянно добавляется и удаляется логика, связанная с профилем.
Что-то взлетает и остается навсегда, а что-то отмирает со временем и остается как рудимент.
При миграции смотрели на нагрузку на таблицу в БД и на использование в коде.

К слову, на старте было 70, а не 50 таблиц.
Но, к сожалению разработчиков самые сложные таблицы для миграции в этот список не попали :)

Экстремальная миграция на PostgreSQL: без остановки, потерь и тестирования

В тексте статьи проблема описана неточно.
Суть проблемы — если запросе есть limit, то планировщик Postgres решает, что ему дешевле искать полным перебором.
В итоге получаем full scan, несмотря на наличие индекса.
Oracle в запросах с limit корректно использует индекс.

Лечится проблема в Postgres:
• либо оборачиванием запроса в set enable_seqscan to off; set enable_seqscan to on;
• либо сортировкой (order by) по полю, для которого есть индекс.

Ссылки по теме:
1) https://www.postgresql.org
2) stackoverflow

Экстремальная миграция на PostgreSQL: без остановки, потерь и тестирования

С точки зрения Postgres хватило текущей функциональности и документации.
А вот по SymmetricDS потребовалась помощь их саппорта.

Экстремальная миграция на PostgreSQL: без остановки, потерь и тестирования

Все неструктурированные данные, которые нельзя красиво уместить в реляционную модель:
— входные параметры платежа
— описание состояния платежа (контексты операций)
— результат платежа
— кастомные параметры профиля пользователя

Размер LOB у нас обычно варьируется от 1 до 8 Кб на одну строку.

Информация

В рейтинге
Не участвует
Откуда
Санкт-Петербург, Санкт-Петербург и область, Россия
Дата рождения
Зарегистрирован
Активность