何时以及如何使用休眠二级缓存?
Posted
技术标签:
【中文标题】何时以及如何使用休眠二级缓存?【英文标题】:When and how to use hibernate second level cache? 【发布时间】:2011-10-26 21:28:27 【问题描述】:我无法理解休眠何时命中二级缓存以及何时使缓存无效。
这是我目前理解的:
二级缓存存储会话之间的实体,范围是 SessionFactory 您必须告诉缓存哪些实体,默认情况下不会缓存任何实体 查询缓存将查询结果存储在缓存中。我不明白的是
hibernate 何时访问此缓存? 假设我设置了二级缓存,但没有设置查询缓存。我想缓存我的客户,他们有 50000 个。我可以通过哪些方式从缓存中检索客户? 我假设我可以通过 id 从缓存中获取它们。这很容易,但也不值得缓存。但是,如果我想对所有客户进行一些计算怎么办。假设我想显示客户列表,那么我将如何访问他们? 如果禁用查询缓存,我将如何获得所有客户? 如果有人更新了其中一位客户会发生什么? 该客户会在缓存中失效还是所有客户都会失效?或者我认为缓存完全错误?在这种情况下,二级缓存更合适的用途是什么?休眠文档根本不清楚缓存在现实中是如何工作的。只有如何设置它的说明。
更新: 所以我开始明白二级缓存(没有查询缓存)将有利于通过 id 加载数据。例如,我有一个用户对象,我想检查 Web 应用程序中每个请求的权限。通过在二级缓存中缓存用户来减少数据库访问是否是一个很好的案例?就像我将用户 ID 存储在会话中或任何地方,当我需要检查权限时,我会通过它的 ID 加载用户并检查权限。
【问题讨论】:
What is second level cache in hibernate?的可能重复 【参考方案1】:首先,让我们谈谈进程级缓存(或在 Hibernate 中称之为二级缓存)。为了让它工作,你应该
-
配置缓存提供程序
告诉 hibernate 缓存哪些实体(如果您使用这种映射,则在 hbm.xml 文件中)。
您告诉缓存提供者它应该存储多少对象以及何时/为什么应该使它们无效。因此,假设您有 Book 和 Author 实体,每次从 DB 中获取它们时,只会从实际 DB 中选择那些不在缓存中的实体。这显着提高了性能。在以下情况下很有用:
您只能通过 Hibernate 写入数据库(因为它需要一种方法来知道何时更改或使缓存中的实体无效) 您经常阅读对象 您只有一个节点,但没有复制。否则,您将需要复制缓存本身(使用 JGroups 等分布式缓存),这会增加复杂性,而且它的扩展性不如无共享应用程序。那么缓存什么时候起作用?
当您session.get()
或session.load()
时,先前选择并驻留在缓存中的对象。缓存是一种存储,其中 ID 是键,属性是值。因此,只有当有可能通过 ID 进行搜索时,您才能避免访问数据库。
当您的关联是延迟加载(或使用选择而不是连接进行急切加载)时
但在以下情况下不起作用:
如果您不按 ID 选择。再次 - 二级缓存存储实体 ID 到其他属性的映射(它实际上并不存储对象,而是数据本身),所以如果你的查找看起来像这样:from Authors where name = :name
,那么你就不会命中缓存。
当您使用 HQL 时(即使您使用 where id = ?
)。
如果在映射中设置了fetch="join"
,这意味着加载关联时将在任何地方使用连接,而不是单独的选择语句。进程级缓存仅在使用 fetch="select"
时才对子对象起作用。
即使您有 fetch="select"
,但在 HQL 中您使用连接来选择关联 - 这些连接将立即发出,它们将覆盖您在 hbm.xml 或注释中指定的任何内容。
现在,关于查询缓存。您应该注意,它不是一个单独的缓存,它是对进程级缓存的补充。假设您有一个 Country 实体。它是静态的,因此您知道每次说from Country
时都会有相同的结果集。这是查询缓存的完美候选者,它本身会存储 IDs 列表,当您下次选择所有国家/地区时,它会将此列表返回到进程级缓存,后者依次返回, 将返回每个 ID 的对象,因为这些对象已经存储在二级缓存中。
每次与实体相关的任何内容发生更改时,查询缓存都会失效。因此,假设您将from Authors
配置为放入查询缓存中。由于作者经常更改,因此它不会有效。所以你应该只对或多或少的静态数据使用查询缓存。
【讨论】:
查询“from Author a fetch join a.books”是否需要查询缓存才能从缓存中获取作者? 不,查询缓存仅用于静态数据,它只存储 ID。作者将从二级缓存中获取。 @ctapobep:你说的不是真的!如果实体作者的字段书籍被注释(获取 EAGER),“从作者获取加入 a.books”工作正常......我认为为时已晚 这么好的答案!我会一直记得的! :d 开启'查询缓存'后,如果你选择了id以外的其他属性,它会从缓存中获取数据吗?【参考方案2】: 二级缓存是键值存储。它仅在您通过 id 获取实体时才有效 当通过休眠更新/删除实体时,每个实体的二级缓存无效/更新。如果数据库以不同的方式更新,它不会失效。 查询(例如客户列表)使用查询缓存。实际上,拥有一个键值对分布式缓存很有用——这就是 memcached,它为 facebook、twitter 等提供了支持。但如果你没有按 id 进行查找,那么它不会很有用。
【讨论】:
查询缓存(适用于 DTO 模式的 Projections、ResultTransformers),如果您更新用于缓存查询的实体,则会失效。正如您所说,二级缓存仅适用于通过 id 获取实体的查询(不适用于条件限制)或带有投影的条件(仅选择某些属性)。顺便说一句,“休眠缓存如何工作”的最佳(恢复)答案。【参考方案3】:参加晚会但想系统地回答许多开发人员提出的这些问题。
在这里一一回答你的问题是我的答案。
问。 hibernate 什么时候访问这个缓存?
A. 一级缓存与Session对象相关联。 二级缓存与会话工厂对象相关联。如果第一层没有找到对象,则检查第二层。
问。假设我设置了二级缓存,但没有设置查询缓存。我想缓存我的客户,他们有 50000 个。我可以通过哪些方式从缓存中检索客户?
A.您在更新中得到了答案。此外,查询缓存仅存储对象的 ID 列表,而这些对象的 ID 存储在 same 二级缓存中。因此,如果您启用查询缓存,您将使用相同的资源。整齐吧?
问。我假设我可以通过 id 从缓存中获取它们。这很容易,但也不值得缓存。但是,如果我想对所有客户进行一些计算怎么办。假设我想显示客户列表,那么我将如何访问他们?
A.上面已经回答了。
问。如果禁用查询缓存,我将如何获得所有客户?
A.上面已经回答了。
问。如果有人更新了其中一位客户会发生什么?该客户会在缓存中失效还是所有客户都会失效?
A. Hibernate 不知道,但您可以使用其他第三方 IMDG / 分布式缓存来实现为 hibernate second level cache 并使它们无效。例如TayzGrid 就是这样一种产品,我猜还有更多。
【讨论】:
【参考方案4】:Hibernate 二级缓存的理解和实现有点棘手。根据您的问题,我们可以说以下内容:
Hibernate 何时访问此缓存?
正如您所建议的,仅在 L1 缓存之后才查询 Hibernate L2 缓存(如果启用;默认情况下未启用)。这是一个键值缓存,其数据跨多个会话保留。
假设我设置了二级缓存,但没有设置查询缓存。我想缓存我的客户,他们有 50000 个。我可以通过哪些方式从缓存中检索客户?
查询缓存最适合此用例,因为客户数据是静态的并且从关系数据库中检索。
如果有人更新了其中一位客户会发生什么?该客户会在缓存中失效还是所有客户都会失效?
这取决于您使用的特定 Hibernate 缓存策略。 Hibernate 实际上有四种不同的缓存策略:
READ_ONLY:对象一旦进入缓存就不会改变。
NONSTRICT_READ_WRITE:对象在相应的数据库条目更新后(最终)发生变化;这保证了最终的一致性。
READ_WRITE:对象在相应的数据库条目更新后(立即)发生变化;这通过使用“软”锁保证了强一致性。
TRANSACTIONAL:使用分布式XA事务改变对象,保证数据完整性;这可以保证完全成功或回滚所有更改。 但是,在所有这四种情况下,更新单个数据库条目不会使缓存中的整个客户列表无效。 Hibernate 比这更聪明一点:)
要了解有关 Hibernate 中 L2 缓存如何工作的更多信息,您可以查看文章“什么是 Hibernate L2 缓存”或深入文章 Caching in Hibernate with Redis
【讨论】:
以上是关于何时以及如何使用休眠二级缓存?的主要内容,如果未能解决你的问题,请参考以下文章
grails 3(spring-boot) - 如何配置hibernate二级缓存