第一部分
在 Grails 应用程序中,我知道您可以通过添加来启用每个域类的二级缓存
static mapping {
cache true
}
默认情况下,仅在调用 get()
时使用二级缓存,但也可以通过添加 cache true
将其用于条件查询和动态查找器查询。
但是,我仍然不确定我是否理解查询缓存的工作原理。我最好的猜测是:
- 每个域类都有单独的查询缓存,例如一个用于书籍,另一个用于作者
- 在执行像
Author.findByName('bob', [cache: true])
这样的查询之前,会计算一个缓存键,该键基于域类 (Author)、查询(findByName) 和查询参数 ('bob')。如果在作者查询缓存中找到该键,则返回缓存结果而不是执行查询 - 每次保存、删除或更新作者时,作者查询缓存都会被刷新
这似乎是合理的,直到我们考虑到返回 Book 实例的查询可能会连接到 Author 表。在这种情况下,当保存、删除或更新作者时,有必要刷新图书和作者查询缓存。这让我怀疑也许只有一个查询缓存,并且每当保存任何缓存的域类时它就会被清除?
第二部分
在 Grails 文档中提到了
As well as the ability to use Hibernate's second level cache to cache instances you can also cache collections (associations) of objects.
例如:
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
}
}
上面的配置是否有意义,即如果作者和书籍本身使用二级缓存,那么使作者-书籍关联也使用二级缓存有什么好处吗?
第三部分
最后,我读了 this advice关于使用二级查询缓存,这表明它应该仅用于不经常更改的域类。是否存在任何情况下不应为 get()
操作启用二级缓存,即不将以下内容添加到域类的任何原因
static mapping = {
cache true // Book uses the 2nd level cache
}
最佳答案
第 1 部分:
Hibernate 做了正确的事。查询缓存不是针对每个实体的。有一个查询缓存区域,由所有查询共享,除非您为查询设置特定区域。每次更新表时,时间戳缓存中的时间戳都会更新。每次执行查询时,都会将查询搜索的每个表的时间戳与缓存结果的时间戳进行比较。当然,只有当缓存结果的时间戳比所有表时间戳更新时,才会返回缓存结果。
第 2 部分:
是的,这是有道理的。作者的缓存记得 ID 为 456 的作者的姓名为“foo”,出生日期为 1975/07/19。仅记住作者表中存储的数据。因此,缓存关联也很有用:Hibernate 将获取作者书籍的 ID,而不是在调用 author.getBooks()
时进行额外的查询来获取作者的书籍集。从其缓存中加载每本书,然后从二级缓存中加载每本书。不过,请确保缓存书籍。
第 3 部分:
我可以想象几个原因:
- 实体如此之多,而且它们的变化如此之大,以至于缓存命中的次数会非常低,而且二级缓存处理实际上会比没有缓存的解决方案消耗更多的时间和内存
- 应用是集群化的,分布式二级缓存的成本和复杂度太高, yield 较低
- 其他非 hibernate 应用程序写入同一数据库,因此缓存存在返回过时数据的巨大风险,这是 Not Acceptable
- 没有二级缓存,一切都会顺利进行,并且没有理由让应用程序变得更加复杂。
关于Grails 应用程序中的 Hibernate 二级缓存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9344907/