NHibernate二级缓存:查询缓存未按预期工作

时间:2019-01-08 15:30:35

标签: caching nhibernate

我使用的软件包:

NHibernate 5.2.1
NHibernate.Caches.SysCache 5.5.1

NH缓存配置:

<configuration>
    <configSections>
        <section name="syscache" type="NHibernate.Caches.SysCache.SysCacheSectionHandler,NHibernate.Caches.SysCache" />
    </configSections>

    <syscache>
        <!-- 3.600s = 1h; priority 3 == normal cost of expiration -->
        <cache region="GeoLocation" expiration="3600" sliding="true" priority="3" />
    </syscache>
</configuration>

我想使用其唯一的主键查询一堆位置。在此单元测试中,我使用不同的会话但使用相同的会话工厂模拟两个请求:

[TestMethod]
public void UnitTest()
{
    var sessionProvider = GetSessionProvider();

    using (var session = sessionProvider.GetSession())
    {
        var locations = session
            .QueryOver<GeoLocation>().Where(x => x.LocationId.IsIn(new[] {147643, 39020, 172262}))
            .Cacheable()
            .CacheRegion("GeoLocation")
            .List();

        Assert.AreEqual(3, locations.Count);
    }

    Thread.Sleep(1000);

    using (var session = sessionProvider.GetSession())
    {
        var locations = session
            .QueryOver<GeoLocation>().Where(x => x.LocationId.IsIn(new[] { 39020, 172262 }))
            .Cacheable()
            .CacheRegion("GeoLocation")
            .List();

        Assert.AreEqual(2, locations.Count);
    }
}

如果以完全相同的顺序查询完全相同的ID,则第二个调用将从缓存中获取对象。但是,在此示例中,仅使用两个先前提交的ID调用查询。尽管位置已被缓存,但是第二个查询将从数据库中获取它们。

我希望缓存能够像首先查询的表一样工作。只有尚未缓存的ID才能触发DB调用。但是显然,整个查询似乎是缓存对象的哈希键。

有什么办法可以改变这种行为?

1 个答案:

答案 0 :(得分:2)

没有部分查询缓存的概念,它是全部或没有:如果找到此 exact 查询的结果-将使用它们,否则将查询数据库。这是因为查询缓存系统没有关于查询含义的特定知识(例如,它无法推断特定查询的结果是某些缓存结果的子集的事实)。

换句话说,NHibernate中的查询缓存充当文档存储而不是关系表存储。文档的关键是查询的SQL(如果是linq,则表示表达式树的某些文本表示形式),所有参数类型和所有参数值的组合。

为解决您的特殊情况,我建议进行一些性能测试。根据测试和数据集的大小,有一些可能的解决方案:在客户端上过滤缓存的结果(如下所示),或者不使用查询缓存,或者可以在应用程序级别为特定查询实现某种缓存机制。

[TestMethod]
public void UnitTest()
{
    var sessionProvider = GetSessionProvider();

    using (var session = sessionProvider.GetSession())
    {
        var locations = session
            .QueryOver<GeoLocation>()
            .Cacheable()
            .CacheRegion("GeoLocation")
            .List()
            .Where(x => new[] {147643, 39020, 172262}.Contains(x.LocationId))
            .ToList();

        Assert.AreEqual(3, locations.Count);
    }

    Thread.Sleep(1000);

    using (var session = sessionProvider.GetSession())
    {
        var locations = session
            .QueryOver<GeoLocation>().
            .Cacheable()
            .CacheRegion("GeoLocation")
            .List()
            .Where(x => new[] {39020, 172262}.Contains(x.LocationId))
            .ToList();

        Assert.AreEqual(2, locations.Count);
    }
}

有关{N}休眠查询缓存的工作方式的更多信息,here