什么时候以及如何使用休眠二级缓存?


90

我无法理解休眠何时进入二级缓存以及何时使缓存失效。

这是我目前所了解的:

  • 第二级缓存在会话之间存储实体,作用域为SessionFactory
  • 您必须告诉要缓存的实体,默认情况下不会缓存任何实体
  • 查询缓存将查询结果存储在缓存中。

我不明白的是

  • 休眠何时会命中此缓存?
  • 假设我已经设置了二级缓存,但没有设置查询缓存。我想缓存我的客户,其中有50000。我可以通过哪些方式从缓存中检索客户?
  • 我想我可以通过缓存从id获取它们。那将是容易的,但也不值得缓存。但是,如果我想对所有客户进行一些计算,该怎么办?假设我要显示客户列表,那么我将如何访问他们?
  • 如果禁用查询缓存,如何获得所有客户?
  • 如果有人更新了一位客户,将会怎样?
    • 客户会在缓存中失效还是所有客户都失效?

还是我认为缓存完全错误?在这种情况下,更适合使用二级缓存?休眠文档根本不清楚高速缓存实际上是如何工作的。只有有关如何进行设置的说明。

更新: 因此,我开始理解二级缓存(不带查询缓存)对于按ID加载数据非常有用。例如,我有一个用户对象,我想检查Web应用程序中每个请求的权限。通过在二级缓存中缓存用户来减少数据库访问是否是一个好案例?就像我将用户ID存储在会话中或在需要检查权限的任何地方一样,我将通过用户的ID加载用户并检查权限。


Answers:


100

首先,让我们谈谈进程级缓存(或在Hibernate中称为二级缓存)。要使其正常工作,您应该

  1. 配置缓存提供程序
  2. 告诉hibernate要缓存哪些实体(如果使用这种映射,则在hbm.xml文件中)。

您告诉缓存提供程序应存储多少个对象以及何时/为什么使它们无效。因此,假设您有一个Book和Author实体,每次从数据库中获取它们时,只会从实际的DB中选择那些不在缓存中的实体。这样可以显着提高性能。在以下情况下很有用:

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

那么什么时候缓存起作用?

  • 当您session.get()session.load()先前选择并驻留在缓存中的对象时。缓存是一种存储,其中ID是键,属性是值。因此,只有在有可能通过ID搜索时,您才可以消除访问数据库的麻烦。
  • 当您的关联被延迟加载(或使用selects而不是joins加载时)

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

  • 如果您不按ID选择。再说一遍-2级缓存将实体ID映射到其他属性(它实际上并不存储对象,但存储数据本身),因此,如果您的查找如下所示:from Authors where name = :name,则不会命中缓存。
  • 使用HQL时(即使使用where id = ?)。
  • 如果在映射中设置fetch="join",这意味着要加载关联,联接将在各处使用,而不是单独的select语句。进程级缓存仅在fetch="select"使用子对象时才起作用。
  • 即使您有,fetch="select"但在HQL中也可以使用联接来选择关联-这些联接将立即发出,并且它们将覆盖您在hbm.xml或注释中指定的内容。

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


查询“从作者获取并连接a.books”查询是否需要查询缓存才能从缓存中获取作者?
palto 2011年

1
不,查询缓存仅用于静态数据,并且仅存储ID。作者将从二级缓存中获取。
Stanislav Bashkyrtsev 2011年

@ctapobep:你说的不对!如果对实体作者的实地图书进行了注释(从EAGER获取注释),则“从作者获取联接a.books”效果很好。我认为为时已晚
Bilal BBB 2015年

很好的答案!我会一直记住它!:d
Mohammadreza Khatami

启用“查询缓存”后,如果您通过id以外的其他属性进行选择,是否会从缓存中获取数据?
阿伦·拉伊

41
  • 第二级缓存是键值存储。仅当您通过ID获取实体时才有效
  • 通过休眠更新/删除实体时,每个实体的2级缓存均无效/更新。如果以其他方式更新数据库,则它不会无效。
  • 对于查询(例如客户列表),请使用查询缓存。

实际上,拥有键值分布式缓存非常有用-这就是memcached,它为Facebook,Twitter等提供了强大的动力。但是,如果您没有按ID进行查找,那么它将不会很有用。


12

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

我的回答是一个接一个地回答您的问题。

问:休眠何时会命中此缓存?

答:一级缓存会话对象相关联。该二级缓存与相关的会话工厂对象。如果在第一层中没有找到对象,则检查第二层。

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

答:您在更新中得到了答案。同样,查询缓存仅存储对象的ID列表,而那些对象的ID则存储在同一二级缓存中。因此,如果启用查询缓存,则将利用相同的资源。整洁吧?

问:我想我可以通过缓存从id获取它们。那将是容易的,但也不值得缓存。但是,如果我想对所有客户进行一些计算,该怎么办?假设我要显示客户列表,那么我将如何访问他们?

答:以上回答。

问:如果禁用查询缓存,如何获得所有客户?

答:以上回答。

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

答:Hibernate不知道,但是您可以使用其他第三方IMDG /分布式缓存来实现为Hibernate二级缓存并使它们无效。例如TayzGrid就是这样一种产品,我想还有更多。


0

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

Hibernate何时会命中此缓存?

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

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

对于这种用例,查询缓存将是最好的,因为客户数据是静态的,并且是从关系数据库中检索的。

如果有人更新了一位客户,将会怎样?客户会在缓存中失效还是所有客户都失效?

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

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

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

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

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

要了解有关L2缓存在Hibernate中如何工作的更多信息,您可以查看文章“什么是Hibernate L2缓存”,或深入的文章《使用Redis在Hibernate中缓存》。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.