Aar*_*ght 14 .net nhibernate syscache2
我有一个相当简单的条件查询来获取子集合,如下所示:
var order = Session.CreateCriteria<Order>()
.Add(Restrictions.Eq("Id", id))
.SetFetchMode("Customer", FetchMode.Eager)
.SetFetchMode("Products", FetchMode.Eager)
.SetFetchMode("Products.Category", FetchMode.Eager)
.SetCacheable(true)
.UniqueResult<Order>();
Run Code Online (Sandbox Code Playgroud)
使用NH Prof,我已经验证这只使用冷缓存只进行一次数据库往返(如预期); 但是,在连续执行时,它仅从Order
缓存中检索,然后使用SELECT(N + 1)为图中的每个子实体命中数据库,如下所示:
Cached query: SELECT ... FROM Order this_ left outer join Customer customer2 [...]
SELECT ... FROM Customer WHERE Id = 123;
SELECT ... FROM Products WHERE Id = 500;
SELECT ... FROM Products WHERE Id = 501;
...
SELECT ... FROM Categories WHERE Id = 3;
Run Code Online (Sandbox Code Playgroud)
等等等等.显然,它不缓存整个查询或图形,只缓存根实体.第一个"缓存查询"行实际上具有join
它应该具有的所有条件 - 它肯定正确地缓存了查询本身,显然不是实体.
我已经尝试过使用SysCache,SysCache2甚至是HashTable缓存提供程序,而且我似乎总是得到同样的行为(NH版本3.2.0).
谷歌搜索出现了一些古老的问题,例如:
然而,这些似乎都很久以前就已得到解决,无论我使用哪个提供商,我都会遇到同样的不良行为.
我已经阅读了关于SysCache和SysCache2的nhibernate.info文档,似乎没有任何我遗漏的内容.我已经尝试为查询中涉及的所有表添加cacheRegion
行Web.config
文件,但它不会更改任何内容(而AFAIK这些元素只是为了使缓存无效,所以无论如何它们都无关紧要).
所有这些超级老问题似乎都得到修复/解决,我认为这不可能仍然是NHibernate中的一个错误,它一定是我做错了.但是什么?
将NHibernate中的提取指令与二级缓存组合时,我需要做些什么特别的事情吗?我在这里错过了什么?
Aar*_*ght 37
我确实设法解决了这个问题,所以其他人终于可以得到一个直接的答案:
总结一下,我对第二级缓存和查询缓存之间的区别感到困惑; 杰森的答案在技术上是正确的,但它不知何故没有点击我.以下是我将如何解释它:
查询缓存会跟踪查询发出的实体.它并没有缓存整个结果集.它相当于Session.Load
在一个懒惰的实体上做一个; 它知道/期望一个存在但不跟踪任何其他有关它的信息,除非特别要求,此时它将实际加载真实实体.
二级缓存跟踪每个实体的实际数据.当NHibernate的需要通过它的ID加载任何实体(凭借着Session.Load
,Session.Get
,延迟加载的关系,或者,在上述的情况下,一个实体"参照",这是一个高速缓存的查询的一部分),它看起来在第二级先缓存.
当然,这在事后看来是完全合理的,但当你听到"查询缓存"和"二级缓存"在很多地方几乎可以互换使用时,它就不那么明显了.
基本上,您需要配置两组两个设置,以便通过查询缓存查看预期结果:
在XML配置中,这意味着添加以下两行:
<property name="cache.use_second_level_cache">true</property>
<property name="cache.use_query_cache" >true</property>
Run Code Online (Sandbox Code Playgroud)
在Fluent NHibernate中,它是这样的:
.Cache(c => c
.UseQueryCache()
.UseSecondLevelCache()
.ProviderClass<SysCacheProvider>())
Run Code Online (Sandbox Code Playgroud)
请注意UseSecondLevelCache
以上内容,因为(在发布时)在Fluent NHibernate wiki页面上从未提及过; 有几个启用查询缓存而不是二级缓存的示例!
只是启用二级缓存几乎没有什么,这就是我被绊倒的地方.必须不仅启用二级缓存,而且还要为要缓存的每个单个实体类进行配置.
在XML中,这是在<class>
元素内部完成的:
<cache usage="read-write"/>
Run Code Online (Sandbox Code Playgroud)
在Fluent NHibernate(非自动化)中,它在ClassMap
构造函数中完成,或者在您放置其余映射代码的任何位置完成:
Cache.ReadWrite().Region("Configuration");
Run Code Online (Sandbox Code Playgroud)
必须为要缓存的每个实体执行此操作.可能可以在一个地方设置作为约定,但是你几乎错过了使用区域的能力(在大多数系统中,你不希望像配置数据一样缓存事务数据).
就是这样.这真的不是那么难,但很难找到一个好的,完整的例子,特别是对于FNH.
最后一点:这样做的自然结果是,当与查询缓存一起使用时,它使得急切的加入/获取策略非常难以预测.显然,如果NHibernate的看到一个查询缓存,它会使没有任何努力首先检查是否所有甚至任何实际的实体缓存.它几乎只是假设它们是,并试图单独加载每个.
这就是SELECT N + 1灾难的原因; 如果NH注意到实体不在二级缓存中并且只是正常执行查询,就像书面一样,使用提取和期货等等,那就没什么大不了的了.但它没有那样做; 相反,它会尝试加载每一个实体,和它的关系,及其子关系,其细分子关系,等等,一次一个.
因此,除非您已经为整个图中的所有实体显式启用了缓存,否则几乎没有必要使用查询缓存,即使这样,您也要非常小心(通过到期,依赖等). )缓存的查询不会超出他们应该检索的实体,否则你最终会使性能变差.
归档时间: |
|
查看次数: |
5051 次 |
最近记录: |