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

我无法理解休眠何时达到第二级缓存以及何时使缓存无效。

这是我目前所理解的:

  • 第二级缓存在会话之间存储实体,作用域是会话因子
  • 您必须知道要缓存哪些实体,默认情况下不会缓存任何实体
  • 查询缓存将查询结果存储在缓存中。

我不明白的是

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

还是我认为缓存完全错误?在这种情况下,第二级缓存的用法更合适吗?休眠文档根本不清楚缓存在现实中是如何工作的。只有关于如何设置它的说明。

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


答案 1

首先,让我们谈谈进程级缓存(或他们在Hibernate中称之为2nd级缓存)。要使它工作,你应该

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

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

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

那么缓存何时工作?

  • 当您或以前选择并驻留在缓存中的对象时。缓存是一种存储,其中 ID 是键,属性是值。因此,只有当有可能按ID搜索时,您才能避免命中数据库。session.get()session.load()
  • 当您的关联是延迟加载的(或预先加载了选择而不是联接)

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

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

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


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

实际上,拥有键值分布式缓存是很有用的 - 这就是memcached,它为Facebook,Twitter等提供支持。但是,如果您没有按id查找,那么它将不是很有用。