Кэш запросов Hibernate - для объектов, не находящихся в кэше 2-го уровня - рискованно? полезно? плохая практика?

Связанный с этим вопросом

Предпосылка:

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

  • Кеш запросов хорош в основном вместе с кешем 2-го уровня
  • Кеш запросов кеширует идентификаторы результатов запросов + параметры
  • Кеш запросов опасен, если база данных была изменена, и она не была отражена в кеше

Вопрос:

У меня есть объект, которого нет в кэше 2-го уровня. Из-за плохого программирования или других ограничений код, который загружает объект, вызывается несколько раз в одном и том же сеансе гибернации. Повторное использование использует HQL-запрос поиска, например

 hibernateTemplate.find("from Foo f where f.bar > ?", bar);

Перед добавлением кеша запросов, если вышеуказанный код был вызван N раз в одном сеансе Hibernate, в базе данных было N обращений

Тогда я хотел посмотреть, что произойдет, если я добавлю кеш запросов:

 Query query = session.createQuery("from Foo f where f.bar > ?");
 query.setCacheable(true);
 query.setParameter(bar);
 query.list();

Когда я добавил кэш запросов, я заметил, что во время одного сеанса hibernate больше не попадает в базу данных N раз, только один раз за сеанс.

  1. Итак, мое первое предположение заключается в том, что Hibernate сначала выполняет поиск в кэше сеанса, а затем в кэше 2-го уровня. Это предположение верно?
  2. Я также предполагаю, что если объект (Foo), который не находится в кэше 2-го уровня, был изменен в базе данных, тогда кэш запросов, будучи пересекающим сессию, вернет неправильные идентификаторы и, следовательно, неправильные объекты. Это верно?
  3. Тогда можно ли с уверенностью сказать, что использование кеша запросов для запросов, которые включают в себя неизменяемую информацию даже для кэшированных объектов не 2L, является хорошей практикой? (например, запрос о том, что его предложение where содержит условие, которое всегда будет возвращать одни и те же результаты, например "select p.ser_num, где p.id =?", когда пары ser_num и id не изменяются после создания)

Кстати, в связанном вопросе утверждается, что кэш запросов не работает в области Session Cache. Я неправильно понимаю это утверждение или что-то еще?

3 ответа

Решение

Кеш запросов - это особый тип кеша 2-го уровня. То, что вы называете кешем 2-го уровня, я бы предпочел называть "кешем объектов".

Комментарии к вашим предположениям:

  • Кеш запросов хорош в основном вместе с кешем 2-го уровня (он же кеш объектов).

Кэш запросов содержит только необработанные результаты запросов в качестве первичных ключей, в режиме гибернации, идентификаторы. Он не содержит реальных гидратированных объектов. Это имеет смысл, поскольку, когда вы выполняете запрос с помощью jdbc, он на самом деле возвращает только гидратированные (заполненные) объекты, когда вы перебираете ResultSet. Утверждение не обязательно правильное. Если запрос очень сложный и, следовательно, занимает очень много времени, используя кеш запросов, вы сэкономите это время. Используя кеш запросов, вы не сэкономите время, необходимое для загрузки объектов из базы данных.

  • Кеш запросов опасен, если база данных была изменена, и она не была отражена в кеше

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

Итак, мое первое предположение заключается в том, что Hibernate сначала выполняет поиск в кэше сеанса, а затем в кэше 2-го уровня. Это предположение верно?

Да, при загрузке объектов это поведение.

Я также предполагаю, что если объект (Foo), который не находится в кеше 2-го уровня, был изменен в базе данных, то кэш запросов, будучи пересекающимся между сеансами, вернет неправильные идентификаторы и, следовательно, неправильные объекты. Это верно?

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

Тогда можно ли с уверенностью сказать, что использование кеша запросов для запросов, которые включают в себя неизменяемую информацию даже для кэшированных объектов не 2L, является хорошей практикой? (например, запрос о том, что его предложение where содержит условие, которое всегда будет возвращать одни и те же результаты, например "select p.ser_num, где p.id =?", когда пары ser_num и id не изменяются после создания)

Для таких объектов нет причин не использовать кеш объектов и кеш запросов.

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

только предположения:

Согласно этой статье из документации Hibernate:

[запрос кеша] создает две новые области кеша: одна содержит кэшированные наборы результатов запроса (org.hibernate.cache.StandardQueryCache), другая - временные метки последних обновлений таблиц с запросами (org.hibernate.cache.UpdateTimestampsCache).

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

Но через 2 предложения в том же абзаце документации написано:

Кэш запросов всегда должен использоваться вместе с кешем второго уровня.

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

Что касается вашего вопроса № 3, я не думаю, что вы хотите использовать кэш запросов, когда объекты не кэшируются. В итоге вы получите все первичные идентификаторы, но для получения объектов, которые могут быть медленнее, чем выполнение запроса без кэширования, придется нажимать базу данных один раз для каждого ключа. В любом случае, начиная с 3.3, возможно, в более новых версиях он захватывает отсутствующие объекты, используя меньше запросов, например, где id в (:id1,:id2,...).

Другие вопросы по тегам