2017-01-30 2 views
2

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

Параметры и требования

Параметры и требования к этому списку являются следующие (я постараюсь упомянуть только важные из них здесь):

  • Там может быть до 500000 строк/объектов в списке.
  • Только пара из них отображается одновременно, здесь мы будем использовать разбивку на страницы.
  • Пользователь может выбрать, какие столбцы должны отображаться в списке (поэтому мы не можем предоставить один «статический» запрос).
  • Большинство столбцов списка сортируются и/или фильтруются.
  • Объекты имеют пару ко многим отношениям, которые также предоставляют некоторые столбцы списка.
  • Некоторые из этих столбцов списка могут содержать несколько значений (они отображаются как список внутри одной ячейки)
  • Любые обновления данных, происходящие из действий текущего пользователя (например, редактирование, загрузка и т. Д.), Должны отражаться в список как можно быстрее (т.е. «immeditately»)

чтобы сделать модель немного яснее рассмотреть следующую упрощенную сущность (как в JPA) модель:

class Car { 
    String manufacturer; 
    String model; 
    Date dateOfProduction; 
    List<TyreSize> allowedTyreSizes; 
    Set<Date> inspectionDates; 
} 

Пожалуйста, не пытайтесь слишком много смысла в этой модели, поскольку это jus t, чтобы проиллюстрировать проблему (наши данные различны и намного сложнее).

А «полный» список автомобилей может выглядеть следующим образом:

+==============+=======+=======+===============+=============+ 
| Manufacturer | Model | Prod. | Allowed Tyres | Inspections | 
+==============+=======+=======+===============+=============+ 
| BMW   | 320d |01/2016| - 225/40 R18 | - 01/07/16 | 
|    |  |  | - 225/45 R17 | - 13/12/16 | 
+--------------+-------+-------+---------------+-------------+ 
| Toyota  | Camry |09/2016| - 185/70 R13 | - 31/12/16 | 
+--------------+-------+-------+---------------+-------------+ 

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

Основная проблема

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

Таким образом, мы изучаем возможности для улучшения сортировки и фильтрации всех этих ADN данных в то время как мы в настоящее время следующий подход делает это на базе данных я все еще задаю этот (сторона) вопрос:

  • Как лучше всего подобрать разбиение на страницы большого количества строк с динамическими столбцами и потенциально несколько значений на ячейку?

В настоящее время мы используем Postgresql и хотели бы продолжать использовать его, если это возможно, но если другое хранилище будет намного лучше, мы бы хотя бы это выяснили.

Текущий подход и вопрос (ы) (в нижней части)

Как описано выше, в настоящее время мы пытаемся иметь сортировки базы данных, фильтр и постраничной наши данные. Было бы хорошо использовать 2 запроса: один для получения идентификаторов строк для текущей страницы, а другой - для фактической загрузки данных для этих строк.

Поскольку задача является первым запрос я сосредоточусь на том, что:

AFAIK мы могли бы сделать что-то вроде этого в SQL (на примере автомобиля выше):

SELECT DISTINCT id FROM (
    SELECT id, ... FROM car c 
    LEFT OUTER JOIN allowedtyresizes ats ON c.id = ats.car_id 
    LEFT OUTER JOIN tyresizes ts ON ts.id = ats.tyresize_id 
    ... //additional joins if required 
    ORDER BY ... //apply any user-defined sorts 
    WHERE ... //apply any user-defined filters (or maybe put them into the joins) 
) 
    OFFSET ... //page offset 
    LIMIT ... //page size 

В теории запроса (возможно, это не совсем корректно) должен предоставить результат, нам нужно определить, какие строки загружать для текущей страницы.

Поскольку мы используем Hibernate (5.2 атм), мы хотели бы использовать HQL или Criteria для достижения этого. Однако кажется, что Hibernate не поддерживает выбор из инструкции select, как указано выше, и, следовательно, это может быть не жизнеспособным подходом. Если нам придется вернуться к родному SQL или совсем другому подходу, пусть будет так, но мы предпочли бы, чтобы он работал с инфраструктурой, которая в настоящее время существует.

Так вопросы:

  • ли Hibernate поддержка 5.x что-то вроде "выбрать из выбора"?
  • Как можно использовать разбиение на страницы с использованием Hibernate, когда сортировка и фильтрация должны выполняться во многих отношениях, то есть где одно соединение может приводить к дублированию строк?
+0

Вы запускаете SQL-запрос с использованием HQL, передавая в него запрос как String –

+0

@GauravSrivastav Да, мы знаем, как выполнить SQL-запрос, и если нет лучшего подхода, мы будем его использовать. Однако, поскольку запросы могут стать довольно сложными и создаваться динамически, нам бы хотелось, чтобы нам не приходилось обращаться к информации сопоставления наших объектов, чтобы извлечь информацию, необходимую для построения такого запроса, а не если Hibernate уже предоставляет что-то, что мы могли бы использовать. – Thomas

+0

Насколько сложна ваша модель?Вероятно, вы получите лучшую производительность, используя представление (ы) базы данных и привязывая сущности к ним. Плохо то, что вам нужно будет вручную написать представление базы данных (и, возможно, другую версию, если вы начнете поддерживать новую RDBMS). Хорошо, что сущности станут проще, а у Hibernate меньше шансов создать плохо исполняемый SQL. –

ответ

2

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

Моим решением было представить Hibernate Search и его интеграцию с ElasticSearch для хранения данных поиска в хранилище данных NoSQL Lucene, что, как вы описали, абсолютно быстро для текстовых запросов на основе юникода и сортировки.

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

Мы работали с 10 миллионами строк и не имели проблем с производительностью. Запрос, выполняющий сортировку и подкачку, в среднем составлял менее 100 миллисекунд.

+0

Thx для информации. У нас фактически есть Hibernate Search уже на месте, просто с использованием Lucence, а не вместо Elastic. Будет ли Elastic улучшать производительность дополнительно или просто упростить доступ к данным? Как вы обрабатываете обновления - обновляете ли вы измененные документы в целом или только те поля, которые были изменены (если это возможно)? Я также предполагаю, что обновления обрабатываются асинхронно, не так ли? – Thomas

+1

Я нашел использование ES проще, поскольку индексы размещались в большей части архитектуры микросервиса, чем встраивание Lucene непосредственно в приложение. У нас также были некоторые проблемы с репликацией индекса между master/slave, которые приводят нас к использованию ES вместо этого. Что касается обновлений, они реплицируются в ES так же, как они сегодня реплицируются в Lucene. Я считаю, что обновления не асинхронны, а вместо этого происходят, когда Hibernate вызывает различные события, которые выполняет поиск. – Naros

+0

Afaik Hibernate Search запускает обновления в конце транзакции, поэтому да, они по сути синхронны по умолчанию. Мы столкнулись с некоторыми проблемами с этим, хотя (в другом проекте), когда большие транзакции требовали от нас очистить кеш первого уровня каждый раз, а затем, что заставляло HS задыхаться от отдельных объектов. Таким образом, мы внедрили там асинхронное обновление и, следовательно, мой вопрос (асинхронные обновления также снизили бы риск отката транзакций, если ES/Lucene недоступен). – Thomas