一尘不染

Grails应用程序中的Hibernate 2级缓存

hibernate

第一部分

在Grails应用程序中,我了解到您可以通过添加以下方式为每个域类启用二级缓存

static mapping {
  cache true
}

默认情况下,仅在get()调用时使用二级缓存,但也可以通过添加cache true到查询将其用于条件查询和动态查找器。

但是,我仍然不确定我是否了解查询缓存的工作原理。我最好的猜测是:

  • 每个域类都有单独的查询缓存,例如一个用于Book,另一个用于Author
  • Author.findByName('bob', [cache: true])执行类似的查询之前,将根据域类(Author),查询(findByName)和查询参数(’bob’)计算缓存键。如果在“作者”查询缓存中找到该键,则返回缓存的结果,而不是执行查询
  • 每当保存,删除或更新作者时,都会刷新作者查询缓存

在我们认为返回Book实例的查询可以加入Author表之前,这似乎是合理的。在这种情况下,有必要在保存,删除或更新作者时同时刷新Book和Author查询缓存。这使我怀疑也许只有一个查询缓存,并且每当保存任何缓存的域类时都将其清除吗?

第二部分

在Grails文档中,它提到

除了使用Hibernate的二级缓存来缓存实例的功能之外,您还可以缓存对象的集合(关联)。

例如:

class Author {

  static hasMany = [books: Book]

  static mapping = { 
    cache true        // Author uses the 2nd level cache
    books cache: true // associated books use the 2nd level cache
  } 
}

class Book {
  static belongsTo = [author: Author]

  static mapping = {
    cache true // Book uses the 2nd level cache
  }
}

上面的配置是否有意义,即如果Author和Book本身使用的是二级缓存,那么使Author-Book关联也使用二级缓存有什么好处吗?

第三部分

最后,我读过这个建议有关使用2级查询缓存,这表明它应该只被用于不频繁变化的领域类。是否有任何情况下不应为get()操作启用二级缓存,即为什么不将以下内容添加到域类的任何原因?

static mapping = {
  cache true // Book uses the 2nd level cache
}

阅读 256

收藏
2020-06-20

共1个答案

一尘不染

第1部分:

Hibernate做正确的事。查询缓存不是每个实体的。除非您为查询设置特定的区域,否则所有查询共享一个查询高速缓存区域。每次更新表时,都会在时间戳缓存中更新其时间戳。每次执行查询时,会将查询搜索到的每个表的时间戳与缓存结果的时间戳进行比较。当然,仅当缓存的时间戳比所有表的时间戳都近时才返回缓存的结果。

第2部分:

是的,这很有道理。作者的高速缓存记住ID为456的作者的名称为“
foo”,出生日期为1975/07/19。只记住存储在作者表中的数据。因此,缓存关联也很有用:author.getBooks()Hibernate不会在调用时进行额外的查询来获取作者的书籍集,而是从其缓存中获取作者的书籍ID,然后从第二个缓存中加载每本书级缓存。不过,请确保缓存书籍。

第3部分:

我可以想象几个原因:

  • 实体太多,而且它们变化如此之大,以至于高速缓存命中的数量将非常低,并且与没有高速缓存的解决方案相比,二级高速缓存处理实际上将消耗更多的时间和内存。
  • 应用程序是集群的,并且分布式二级缓存的成本和复杂性过高,导致收益较低
  • 其他非hibernate应用程序写入同一数据库,因此缓存具有返回陈旧数据的巨大风险,这是不可接受的
  • 如果没有二级缓存,一切都会很好地进行,并且没有理由使应用程序比原来的更加复杂。
2020-06-20