何时以及如何使用hibernate二级缓存?

时间:2011-08-14 18:34:53

标签: java hibernate caching second-level-cache

我很难理解hibernate何时命中二级缓存以及何时使缓存无效。

这就是我目前所理解的:

  • 二级缓存在会话之间存储实体,范围是SessionFactory
  • 您必须告诉哪些实体要缓存,默认情况下不会缓存任何实体
  • 查询缓存将查询结果存储在缓存中。

我不明白的是

  • hibernate何时点击此缓存?
  • 假设我已经设置了二级缓存,但没有设置查询缓存。我想缓存我的客户,其中有50000个。我可以通过哪些方式从缓存中检索客户?
  • 我假设我可以通过缓存中的id获取它们。这很容易但也不值得缓存。但是,如果我想与所有客户进行一些计算,该怎么办?假设我想显示一个客户列表,然后我将如何访问它们?
  • 如果禁用查询缓存,我将如何获得所有客户?
  • 如果有人更新了其中一位客户,会发生什么?
    • 该客户是否会在缓存中失效或所有客户都会失效?

或者我认为缓存完全错误?在这种情况下,哪种更适合使用二级缓存? hibernate文档根本不清楚缓存如何在现实中工作。只有如何设置它的说明。

更新 所以我开始明白,二级缓存(没有查询缓存)可以很好地通过id加载数据。例如,我有用户对象,我想检查Web应用程序中每个请求的权限。通过在二级缓存中缓存用户来减少数据库访问是否是一个好例子?就像我将用户ID存储在会话中或我需要检查权限的任何地方,我会通过它的id加载用户并检查权限。

4 个答案:

答案 0 :(得分:90)

首先,让我们来谈谈进程级缓存(或者在Hibernate中称之为二级缓存)。为了使它工作,你应该

  1. 配置缓存提供程序
  2. 告诉hibernate要缓存哪些实体(如果使用这种映射,则在hbm.xml文件中)。
  3. 告诉缓存提供程序它应该存储多少个对象以及何时/为什么它们应该被无效。因此,假设您有一个Book和Author实体,每次从DB获取它们时,只会从实际DB中选择那些不在缓存中的实体。这显着提高了性能。它在以下情况下很有用:

    • 您只能通过Hibernate写入数据库(因为它需要一种方法来知道何时更改或使缓存中的实体无效)
    • 您经常阅读对象
    • 您有一个节点,并且没有复制。否则,您将需要复制缓存本身(使用像JGroups这样的分布式缓存),这会增加复杂性,并且它不会像无共享应用程序那样扩展。

    那么缓存何时起作用?

    • 当您session.get()session.load()之前选择的对象并驻留在缓存中时。缓存是一个存储,其中ID是键,属性是值。因此,只有当有可能通过ID搜索时,您才能消除对数据库的攻击。<​​/ li>
    • 当您的关联延迟加载(或者使用选择而不是加入时加载时)

    但是在以下情况下它不起作用:

    • 如果您没有按ID选择。再次 - 第二级缓存将实体ID的映射存储到其他属性(它实际上并不存储对象,而是数据本身),因此如果您的查找如下所示:from Authors where name = :name,那么您不会高速缓存中。
    • 使用HQL时(即使您使用where id = ?)。
    • 如果在映射中设置了fetch="join",这意味着要加载关联连接将在任何地方使用,而不是单独的select语句。仅当使用fetch="select"时,进程级缓存才对子对象起作用。
    • 即使你有fetch="select"但是在HQL中你使用连接来选择关联 - 这些连接将立即发布,它们将覆盖你在hbm.xml或注释中指定的任何内容。

    现在,关于查询缓存。您应该注意,它不是一个单独的缓存,它是进程级缓存的补充。假设您有一个国家/地区实体。它是静态的,所以你知道每次当你说from Country时会有相同的结果集。这是查询缓存的理想选择,它将自己存储 ID 列表,当您下次选择所有国家/地区时,它会将此列表返回到进程级缓存,而后者又将,将返回每个ID的对象,因为这些对象已存储在二级缓存中。 每次与实体相关的任何内容发生变化时,查询缓存都会失效。因此,假设您已将from Authors配置为查询缓存。由于作者经常更改,因此无效。因此,您应该仅将查询缓存用于更多或更少的静态数据。

答案 1 :(得分:36)

  • 二级缓存是一个键值存储。它仅适用于您通过id
  • 获取实体的情况
  • 当通过hibernate更新/删除实体时,每个实体使第二级缓存无效/更新。如果以不同的方式更新数据库,则不会失效。
  • 用于查询(例如客户列表)使用查询缓存。

实际上,拥有一个键值分布式缓存很有用 - 这就是memcached,它支持facebook,twitter等等。但是如果你没有id的查找,那么它就没有用了。

答案 2 :(得分:11)

晚会但想要系统地回答许多开发人员提出的问题。

在这里逐一提出你的问题是我的答案。

  

Q值。什么时候hibernate命中了这个缓存?

一个。 第一级缓存会话对象相关联。 二级缓存会话工厂对象相关联。如果在第一个中找不到对象,则检查第二个级别。

  

Q值。假设我已经设置了二级缓存,但没有设置查询缓存。我想缓存我的客户,其中有50000个。我可以通过哪些方式从缓存中检索客户?

一个。您在更新中得到了答案。此外,查询缓存仅存储对象的ID列表,并且这些对象w.r.t它们的ID存储在相同的二级缓存中。因此,如果启用查询缓存,则将使用相同的资源。干得好吗?

  

Q值。我假设我可以通过缓存中的id获取它们。这很容易但也不值得缓存。但是,如果我想与所有客户进行一些计算,该怎么办?假设我想显示一个客户列表,然后我将如何访问它们?

一个。以上回答。

  

Q值。如果禁用查询缓存,我将如何获得所有客户?

一个。以上回答。

  

Q值。如果有人更新了其中一个客户,会发生什么?该客户是否会在缓存中失效或所有客户都会失效?

一个。 Hibernate不知道,但你可以使用其他第三方IMDG /分布式缓存实现hibernate second level cache并使它们失效。例如TayzGrid就是这样一种产品,我想还有更多。

答案 3 :(得分:0)

Hibernate二级缓存在理解和实现方面有些棘手。根据您的问题,我们可以说以下话:

Hibernate何时命中此缓存?

如您所建议,仅在L1缓存之后才查询Hibernate L2缓存(如果启用;默认情况下未打开)。这是一个键值缓存,其数据跨多个会话保留。

比方说,我已经设置了二级缓存,但没有设置查询缓存。我想缓存我的客户,其中有50000。我可以通过哪些方式从缓存中检索客户?

对于此用例而言,查询缓存将是最佳选择,因为客户数据是静态的,并且是从关系数据库中检索的。

如果有人更新了一位客户,将会发生什么?该客户会在缓存中失效还是所有客户都失效?

这取决于您使用的特定Hibernate缓存策略。 Hibernate实际上有四种不同的缓存策略:

READ_ONLY :对象在缓存中不会更改。

NONSTRICT_READ_WRITE :对象(最终)在相应数据库条目更新后发生更改;这保证了最终的一致性。

READ_WRITE :对象在相应数据库条目更新后立即更改;这可以通过使用“软”锁保证强大的一致性。

交易:使用分布式XA事务更改对象,从而确保数据完整性;这样可以保证完全成功或回滚所有更改。 但是,在所有这四种情况下,更新单个数据库条目都不会使高速缓存中的整个客户列表无效。 Hibernate比这还聪明:)

要了解有关L2缓存如何在Hibernate中工作的更多信息,请查看文章“什么是Hibernate L2缓存”或深入的文章Caching in Hibernate with Redis