NHibernate SysCache2和查询缓存-不能避免Select N+1

本文关键字:不能 能避免 Select N+1 缓存 SysCache2 查询 NHibernate | 更新日期: 2023-09-27 17:51:01

我正在使用第二级缓存提供程序SysCache2与Fluent NHibernate,使用标准的流畅配置(启用查询缓存似乎是一般的建议),并以通常的方式定义基于表的依赖关系。

流利:

config.Cache(c => c
 .ProviderClass<NHibernate.Caches.SysCache2.SysCacheProvider>()
 .UseQueryCache()
 .UseSecondLevelCache()
 .UseMinimalPuts()

)

网络。配置:

  <cacheRegion name="User" relativeExpiration="7200">
<dependencies>
  <tables>
    <add name="User" databaseEntryName="OldClient" tableName="tbl_users" />
  </tables>
</dependencies>

用户映射:

public class UserMap : ClassMap<User>
{
    public UserMap()
    {
        Table("Users");
        ....
        // caching
        Cache.IncludeAll().ReadWrite().Region("Users");
    }
}

对于单个请求,一切都按预期运行,即:

Session<User>.Get()

就像NHibernate通过ID缓存的方式一样。后续请求不会访问数据库,使数据库中的记录无效将导致Entity无效,从而导致在下一次请求期间进行后续SQL调用。所有好的。

问题在于查询缓存。在第一种情况下,一切都很好。执行如下调用:

Session<User>.Query(item => item.Active == true) 

会像您期望的那样产生SQL调用(SELECT * FROM Users WHERE Active = true)。随后的执行不会产生SQL。太好了。直到数据库中查询集中的单个记录发生更改。然后执行相同的查询,结果是SELECT N+1:

SELECT * FROM Users WHERE ID = 1
SELECT * FROM Users WHERE ID = 2
SELECT * FROM Users WHERE ID = 3
SELECT * FROM Users WHERE ID = 4
...

我在其他地方找到了参考,尽管没有解决方案:

StackOverflow -我如何使NHibernate缓存获取子集合?参见

Ayende缓存策略他在结尾提到确保"实体也被缓存"

目前我可以避免这种情况的唯一方法是在每个请求之后清除查询缓存-这几乎使它毫无用处。当一个特定的实体失效时,我需要为查询缓存清除所有的东西,而不仅仅是单个记录。

任何想法?

NHibernate SysCache2和查询缓存-不能避免Select N+1

根据你发布的两个链接,查询缓存缓存从查询产生的id,并开始逐个抓取实体:如果你在缓存中有它们,很好,如果没有,运气不好,你现在有很多选择。

在这一点上,我假定您选择的实体不在第二级缓存中(不再?)。我从来没有使用过基于表的依赖关系,我们使用的是基于命令的依赖关系,我刚刚测试了,如果一个实体被标记为无效,只有这个缓存的实体,而不是整个缓存区域,被抛出缓存。

检查配置,以确保它是正确的,也许当你认为你击中第二级缓存,你实际上击中会话缓存?请确保您正在测试缓存在不同的会话中工作。

无论如何,我建议你查看NHibernate的DEBUG级别日志,看看第二级缓存实际发生了什么,在那里你将能够看到所有缓存命中/未命中。我可以给您的另一个技巧是在配置中启用generate_statistics标志。流利的:

config.ExposeConfiguration(c => c.SetProperty("generate_statistics", "true"));
在此之后,您可以访问会话工厂中感兴趣的数据,例如Session.SessionFactory.Statistics.SecondLevelCacheMissCountSession.SessionFactory.Statistics.SecondLevelCacheHitCount。结合使用这两种方法,您应该能够查明问题的原因。