Comments 56
Что-то взлетает и остается навсегда, а что-то отмирает со временем и остается как рудимент.
При миграции смотрели на нагрузку на таблицу в БД и на использование в коде.
К слову, на старте было 70, а не 50 таблиц.
Но, к сожалению разработчиков самые сложные таблицы для миграции в этот список не попали :)
Согласен насчет мобильности psql, помню когда плотно работал с оракл, немного страдал от того, что было проблематично взять работу с собой
— входные параметры платежа
— описание состояния платежа (контексты операций)
— результат платежа
— кастомные параметры профиля пользователя
Размер LOB у нас обычно варьируется от 1 до 8 Кб на одну строку.
Не лучше ли подобные данные хранить в json(b) или xml? Или они настолько неструктурированы, что овчинка выделки не стоит?
Меня больше удивляет вот что. Если их купил СБ, то почему не послал людей из СБТ посмотреть, на чем крутится купленная поделка? В СБТ уйма грамотного народа. После заключения СБТ этих самодельщиков просто бы по-тихому разогнали и переписали бы все по-человечески, все-таки деньги. Где внутренний аудит СБ?
Разумеется, переключение связанных таблиц происходило одновременно, с учетом явной связи в БД — join и неявной — по коду.
Что касается логики в БД, то мы принципиально против такого подхода в своей системе.
Oracle хороший мощный инструмент. Особенности нашей работы не позволяют использовать все его возможности. Нам важны производительность сервиса, утилизация железа, возможность дешевого масштабирования и применение автоматического тестирования.
Все это зачастую закрывает для нас такие теоретические подходы как нормальная форма, логические связи в бд и даже внешние ключи.
В процессе миграции часть из 50 таблиц работала на Oracle, другая — на PoPostgreSQL
То есть таблицы, которые в запросах join'ятся, переносились вместе? А что если большинство таблиц друг с другом соединяются во многих запросах, как бы тогда выглядел сценарий переноса? Это вроде бы нормальная ситуация для нормализованной базы
так как в Oracle отсутствие признака означает вывод в хронологическом порядке.Об этом как-то упомянуто в документации? Насколько мне известно, отсутствие order by означает только одно — то что сервер имеет право вывести данные в любом порядке. Один раз я даже на это крупно напоролся. На Oracle.
Суть проблемы — если запросе есть 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 нельзя рассчитывать на порядок выборки без order by, а в том, что и в Oracle этого нельзя делать! Никогда! Даже если, по каким-то причинам (например из за наличия в запросе группировки), оптимизатор выбирает план, приводящий к выводу строк в требуемом (например «хронологическом») порядке, никак нельзя рассчитывать на то, что он будет использовать этот план всегда. В какой-то момент план изменится и вся логика, завязанная на порядок выборки строк сломается! И невозможно сказать, в какой момент это произойдёт! Если вам требуется порядок выборки — используйте order by.
Без явного указания order by надеяться, что порядок данных всегда будет одним и тем же, крайне оптимистично.
На 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
Это конечно немного другая история, согласен.
приоритетам как и в Oracle ROWNUM = 1 c order by?
странный способ стабилизации плана выполнения — уж лучше запрещенные хины в Oracle
Никогда не пишите так в Oracle.
Лечение заключается в прямом указании Postgres на использование нужного индекса путем добавления сортировки, которая не требуется с точки зрения бизнес-логики клиента:
Silron, т.е. вам нужны случайные записи?
недавно столкнулись, что выборка
select * from (
select * from ...
order by ...
)
where rownum<10;
на 12.1 стала иногда выдавать результаты не в порядку внутреннего order by, хотя план запроса не не изменился.
Пришлось добавлять внешний order by.
Если НСИ потребуют остановки, то наверное, большую часть операций, можно перегрузить «в фоне».
Что касается описанной в статье ситуации, то мы и перегружали данные в фоне, а потом делали обновления в обе БД. И только после этого выполняли переключение
Все же такой опыт поможет сломать недоверие некоторых заказчиков к опен-сорс ПО.
Т.е. это определенное доверие опен-сорс продукту.
Мне вот сложно обосновать, почему заказчик может использовать Постгрес, а не, например, mssql. Точнее обосновать то я могу. Но доверие берется как раз на примере таких переходов.
По сути вы использовали обычные техники zero downtime deployment для деплоев с изменением схем баз данных, с тем лишь нюансом, что новая и старая схема оказались на разных СУБД?
Учитывая что код разрабатывался годами и разными людьми, такая миграция представляет ещё большой риск.
И наша статья про то, как давно живущее приложение аккуратно перенести в сжатый срок на новое окружение с полной миграцией всех данных без прерывания процесса.
Понятно, что нужна дополнительная обвязка в коде, но логически, по-моему, код, который работает одновременно с двумя БД одного типа не сильно отличается от кода, который работает с двумя БД разного (но идеологически схожего) типа. Задача переноса без даунтайма с БД одного типа на БД другого типа, конечно, сложнее, но пока не доходит до специфических команд, то концептуально она не сильно отличается от переноса между двумя БД одного типа, если не пользоваться встроенными инструментами СУБД для этого. Те же принципы, основной из которых, то, что код клиента при переходе модифицируется поэтапно:
- читаем из старой, пишем в старую (исходная точка)
- читаем из старой, пишем в обе
- читаем из новой, пишем в обе (необязательный в целом шаг, но уменьшает риски)
- читаем из новой, пишем в новую (конечная точка)
По аналогии с SymmetricDS, очередь выполняла не только синхронизацию новых и измененных данных, но и начальную массовую загрузку исторических данных в батч режиме.
Особенно важно при работе с критичными таблицами для нас было иметь возможность отката на Oracle без потери данных после переключения на Postgres. Несколько раз мы воспользовались этой функциональностью, когда Postgres не вытянул нагрузку и пришлось переключаться обратно, до решения проблемы.
SymmetricDS мощная штука. Мы на нём ДВХ начали делать.
Вы опенсорс версией пользовались или покупали полный комплект инструментария и поддержки? Там, помнится, тысячи долларов за год.
P.S. Чувствую в РФ скоро будет больше запросов о миграции, чем когда-либо, так что статья в тему сегодняшнего дня.
Пытаюсь отправить деньги через форму-приниматель на сайте, и если платеж осуществляется с карты, то в 90% случаях получаю ошибку. Ошибки различные, от чего зависит — непонятно.
Платежи делал с разных карт разных банков. На разные счета в Яндексе.
Причем платеж Яндекс-деньгами всегда проходит, проблема только при платеже картой.
Опять же, при платеже не через форму-приниматель на Яндекс-кошелек, а через Яндекс-кассу юрлицу платежи картой проходят нормально.
Около двух лет пользуюсь формой яндекса на сайте, ранее проблем не было.
Экстремальная миграция на PostgreSQL: без остановки, потерь и тестирования