Caching NHibernate二级缓存:查询缓存不';I don’我没有按预期工作

Caching NHibernate二级缓存:查询缓存不';I don’我没有按预期工作,caching,nhibernate,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" /> <

我使用的软件包:

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);
    }
}
[TestMethod]
公共空单元测试()
{
var sessionProvider=GetSessionProvider();
使用(var session=sessionProvider.GetSession())
{
变量位置=会话
.QueryOver().Where(x=>x.LocationId.IsIn(新[]{14764339020272262}))
.Cacheable()
.CacheRegion(“地理位置”)
.List();
Assert.AreEqual(3,位置.计数);
}
睡眠(1000);
使用(var session=sessionProvider.GetSession())
{
变量位置=会话
.QueryOver().Where(x=>x.LocationId.IsIn(新[]{39020272262}))
.Cacheable()
.CacheRegion(“地理位置”)
.List();
Assert.AreEqual(2,位置.计数);
}
}
如果以完全相同的顺序查询完全相同的ID,则第二个调用将从缓存中获取对象。但是,在本例中,仅使用两个先前提交的ID调用查询。尽管这些位置已经缓存,但第二个查询将从数据库中获取它们

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


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

没有部分查询缓存的概念,它要么全有,要么全无:如果找到了这个精确查询的结果,就会使用它们,否则就会查询数据库。这是因为查询缓存系统没有关于查询含义的特定知识(例如,它无法推断特定查询的结果是某个缓存结果的子集)

换句话说,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);
    }
}
[TestMethod]
公共空单元测试()
{
var sessionProvider=GetSessionProvider();
使用(var session=sessionProvider.GetSession())
{
变量位置=会话
.QueryOver()
.Cacheable()
.CacheRegion(“地理位置”)
.List()
.Where(x=>new[]{14764339020272}.Contains(x.LocationId))
.ToList();
Assert.AreEqual(3,位置.计数);
}
睡眠(1000);
使用(var session=sessionProvider.GetSession())
{
变量位置=会话
.QueryOver()。
.Cacheable()
.CacheRegion(“地理位置”)
.List()
.Where(x=>new[]{39020272262}.Contains(x.LocationId))
.ToList();
Assert.AreEqual(2,位置.计数);
}
}

关于(N)Hibernate查询缓存如何工作的更多信息,可以找到。

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

换句话说,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);
    }
}
[TestMethod]
公共空单元测试()
{
var sessionProvider=GetSessionProvider();
使用(var session=sessionProvider.GetSession())
{
变量位置=会话
.QueryOver()
.Cacheable()
.CacheRegion(“地理位置”)
.List()
.Where(x=>new[]{14764339020272}.Contains(x.LocationId))
.ToList();
Assert.AreEqual(3,位置.计数);
}
睡眠(1000);
使用(var session=sessionProvider.GetSession())
{
变量位置=会话
.QueryOver()。
.Cacheable()
.CacheRegion(“地理位置”)
.List()
.Where(x=>new[]{39020272262}.Contains(x.LocationId))
.ToList();
Assert.AreEqual(2,位置.计数);
}
}

有关(N)Hibernate查询缓存如何工作的更多信息,请参阅。

在较低级别上打开NH的日志记录可能是值得的。它可以给你一些关于它在幕后做什么的线索,也值得尝试一个显式的事务。请参阅此链接:>是否有任何方法可以更改该行为?没有。@DavidOsborne我试过了,但事务似乎对缓存行为没有影响。我将按照您的建议,尝试使用NH profiler和日志获取一些见解。@hazzik,您有什么信息或链接吗