Борьба за оптимизацию N + 1 запроса в Hibernate - PullRequest
6 голосов
/ 12 июля 2011

Я пытаюсь улучшить n + 1 запрос для проекта, над которым я работаю. Я использую Hibernate с моделью, показанной ниже, и хочу выразить запрос для извлечения всех элементов, связанных с портфелем, включая последние две цены на каждый элемент (цена на данную дату и предыдущую цену).

enter image description here

Пример API:

List<Items> items = findItemsWithLatestTwoPrices(portfolio, latestPriceDate);

В настоящее время я использую один запрос для извлечения всех элементов, связанных с портфелем, а затем перебираю эти элементы для запроса двух последних цен на данный элемент (так что n + 1).

Я пытался выразить это в нативном sql, используя коррелированный подзапрос, но производительность была ужасной. Это и тот факт, что каждый день появляются новые цены (поэтому запрос становится все медленнее), заставляет меня думать, что мне нужна другая модель, но я изо всех сил пытаюсь придумать модель, которая является достаточно эффективной и постоянной с течением времени, так как количество цен увеличивается.

Я думал о различных решениях, включая представление цен в виде связанных списков или использование какого-то рода деревьев, но я считаю, что есть лучшие альтернативы. Я что-то упускаю из виду? Кто-нибудь, работающий над подобной проблемой, придумал хорошее решение?

Меня не волнует, использую ли я HQL или нативный SQL, если производительность приличная. Я также открыт для внесения изменений в модель.

Спасибо!

[Edit]

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

Ответы [ 4 ]

0 голосов
/ 26 июля 2011

Если вы используете Postgre или Oracle, вы можете легко использовать аналитическую / оконную функцию для этих цен, когда вы присоединяетесь к ним, получая первые два значения.Пока колонка для ORDER BY проиндексирована, это должно обеспечить достаточно хорошую производительность.

PS В следующий раз, если вы скажете, что рассматриваете возможность использования собственного SQL - добавьте поставщика / версию БД.

0 голосов
/ 13 июля 2011

Вы можете попробовать несколько вариантов

  1. Поскольку ваши цены основаны на датах, вы можете посмотреть на распределение данных в БД по месяцам.Это значительно поможет вашим запросам, так как количество записей для поиска цены значительно уменьшится, вместо того, чтобы просматривать цены за 2 года.Попробуйте запрос SQL после этого.Также запустите объяснение, чтобы убедиться, что вы используете правильные индексы и т. Д.
  2. Рассматривали ли вы кеширование (например, Memcache)?Вы можете предварительно загрузить ваши цены на товары для текущей и предыдущей цены в кеш.Затем вы можете получить портфель, товары и поисковый кеш по ценам, которые должны быть довольно быстрыми.
0 голосов
/ 14 июля 2011

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

Для получения последних призов вы пробовали установить размер партии для отношения? Сделайте упорядоченное отношение (последний сверху), а затем установите размер пакета равным 10. Это сделает запрос Hibernate для 10 и 10 строк, и с индексами на внешнем ключе и столбце порядка, он должен работать нормально в большинстве случаи.

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

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

0 голосов
/ 12 июля 2011

Вы должны попытаться получить товары И цены в одном запросе. Если вы это сделаете, вы можете перебирать свои товары и их цены, не делая выбор для каждого товара. Ваша проблема с n + 1 должна исчезнуть.

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

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

...