Почему кэширование запросов с помощью Hibernate делает запрос в десять раз медленнее? - PullRequest
8 голосов
/ 21 мая 2009

В настоящее время я экспериментирую с EJB3 в качестве предисловия к крупному проекту на работе. Одна из вещей, которые я изучаю, - это кэширование запросов.

Я создал очень простую модель предметной области с аннотациями JPA, бизнес-интерфейс @Local и реализацию @Stateless в EJB-JAR, развернутую в EAR вместе с очень простым веб-приложением для базового тестирования. EAR развернут в конфигурации по умолчанию JBoss 5.0.1 без изменений. Это было очень просто и сработало, как и ожидалось.

Однако мой последний тест включал кэширование запросов, и я получил странные результаты:

  • У меня есть класс домена, который отображает только идентификатор и значение String, и создал около 10000 строк в этой конкретной таблице
  • В бизнес-компоненте есть очень простой запрос: SELECT m FROM MyClass m
  • Без кеша это выполняется в среднем за 400 мс
  • При включенном кеше запросов (с помощью подсказок к запросу) первое выполнение, конечно, занимает немного больше времени, около 1200 мс. Следующие казни занимают в среднем 3500мс!

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

Это, безусловно, объясняет медленное время выполнения, но кто-нибудь может сказать мне, почему это происходит?

1 Ответ

16 голосов
/ 21 мая 2009

Способ работы кэша запросов заключается в том, что он кэширует только ID объектов, возвращаемых запросом. Итак, ваш первоначальный оператор SELECT может вернуть все объекты, и Hibernate вернет их вам и запомнит идентификаторы.

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

Теперь, прежде чем вы подумаете, что «эта функция явно не работает», причина, по которой она работает таким образом, заключается в том, что Query Cache предназначен для работы совместно с кэшем второго уровня. Если объекты сохраняются в кеше L2 после первого запроса, Hibernate будет искать их там, чтобы удовлетворить запросы для каждого ID.

Я настоятельно рекомендую вам взять книгу Сохранение Java с Hibernate , чтобы узнать больше об этом. В частности, глава 13 посвящена оптимизации запросов и тому, как эффективно использовать кэш.

...