使用二级缓存时,nhibernate查询缓存和实体缓存之间有什么区别?

时间:2014-04-06 00:20:08

标签: caching nhibernate fluent-nhibernate

我正在尝试设置nhibernate二级缓存and i see in this article,,我试图理解查询缓存和实体缓存之间的区别。它说你需要添加

    Cache.ReadOnly();  or Cache.ReadWrite();

对每个实体映射都是这样的:

public class CountryMap : ClassMap<country>
{
   public CountryMap()
   {
      Table("dropdowns");
      Id(x => x.Id, "pkey");
       Map(x => x.Name, "ddlong");
      Map(x => x.Code, "dddesc");
       Where("ddtype = 'COUNTRY'");
       //Informing NHibernate that the Country entity itself is cache-able.
       Cache.ReadOnly();
   }

}

但是当使用nhibernate探查器时,我看到有些东西撞到了二级缓存,我没有设置这个Cache.ReadOnly()值。

这真的需要吗?我是否应该为每个实体做这件事(无论该实体多久改变一次?)。

如果答案是肯定的,我应该为所有实体执行此操作,我看到一个页面提到存在使用此行设置实体的风险,因为它可能导致选择n + 1查询问题,如果您是尝试将该实体与查询中的其他实体联系起来。我正在使用nhibernate profiler,看起来有些东西只是从下面的代码中击中二级缓存。在我的会话设置中,我有以下代码:

  return configuration
            .Mappings(m => m.FluentMappings.AddFromAssemblyOf<ApplicationMap>().Conventions.Add(typeof(Conventions)))
            .ExposeConfiguration(
                c => {
                    c.SetProperty("cache.provider_class", "NHibernate.Caches.SysCache.SysCacheProvider, NHibernate.Caches.SysCache");
                    c.SetProperty("cache.use_second_level_cache", "true");
                    c.SetProperty("cache.use_query_cache", "true");
                    c.SetProperty("expiration", "86400");
                })
            .BuildSessionFactory();

我有一个通用的“查询”方法来执行此操作:

   ICriteria c = Session.CreateCriteria(typeof(T));
   c.SetCacheable(true);
   return c.Future<T>().AsQueryable();

所以基本上我正在尝试确认我是否正确设置了缓存,因为当我使用nhibernate profiler但我没有在实体映射代码中设置Cache时,我看到一些二级缓存命中。我正在尝试确定是否还有其他事情需要做以使缓存工作(或更好地工作)

当我使用nhibernate profiler(没有在实体级别设置Cache.ReadWrite())时,它似乎仍然达到了二级缓存。 (见下面的截图)

enter image description here

3 个答案:

答案 0 :(得分:12)

查询缓存仅存储作为查询结果返回的实体的标识符。实际实体存储在实体缓存区域中。因此,必须将实体配置为可缓存以与查询缓存一起使用。如果在未设置实体可缓存的情况下使用查询缓存,则仍然只有查询结果的标识符将存储在查询缓存中。正如博客中所述

  

查询缓存不会缓存实际实体的状态   结果集;它仅缓存标识符值和值的结果   类型。因此查询缓存应始终与。一起使用   二级缓存。

当重新执行相同的查询时,NHibernate所做的是,它从查询缓存获取查询结果中的标识符列表,并从实体缓存中获取每个实体,如果在缓存中找不到,则从数据库中查询该元素(最终在多个查询中;每个实体一个)。

因此,始终建议对查询缓存中的实体使用二级缓存,即您需要在实体映射中指定Cache.ReadOnly();Cache.ReadWrite();,否则查询缓存将进一步降低您的应用程序性能针对一个缓存查询结果的数据库查询。

答案 1 :(得分:9)

我想提供一些关于我们在NHibernate中拥有的(第二级)缓存选项的摘要。首先,有4种设置。事实上,这些确实代表了非常精细的缓存设置的真正力量。

  1. <property name="cache.use_second_level_cache"> - 全局切换
  2. <property name="cache.use_query_cache"> - 全局切换
  3. <cache usage="read-write" region="xxx"/> - 班级/实例级别
  4. .SetCacheable(true).SetCacheMode(CacheMode.Normal).SetCacheRegion("yyy") - 每个查询
  5. 前两个是全局启用程序/禁用程序。在我们使用下一个/最后两个设置之前,必须先打开它们。

    但事实上,已经有了答案。全球手段 - 支持缓存,本地方式 - 决定1)如何 2)哪个类/查询处理 - 如果愿意或 at所有

    例如,这是SessionFactory.cs

    的摘录
    ...
    bool useSecondLevelCache = PropertiesHelper
            .GetBoolean(Environment.UseSecondLevelCache, properties, true);
    bool useQueryCache = PropertiesHelper
            .GetBoolean(Environment.UseQueryCache, properties);
    
    if (useSecondLevelCache || useQueryCache)
    {
        // The cache provider is needed when we either have second-level cache enabled
        // or query cache enabled. Note that useSecondLevelCache is enabled by default
        settings.CacheProvider = CreateCacheProvider(properties);
    }
    else
    ...
    

    让我明确指出评论:

      

    当我们启用了二级缓存时,需要缓存提供程序     或启用查询缓存。 请注意,默认情况下启用useSecondLevelCache

    (注意:另请注意,第一个PropertiesHelper.GetBoolean()来电通过了上一个默认值 true

    这意味着,如果3.设置<cache usage="read-write" region="xxx"/>不重要...所有映射的实例都将被缓存...非托管,默认方式..

    幸运的是,这不是真的。 3.设置,班级,很重要。这是必须的。没有这样的明确设置:

    // xml
    <class name="Country" ...>
      <cache usage="read-write" region="ShortTerm" include="non-lazy/all"/>
    
    // fluent
    public CountryMap()
    {
        Cache.IncludeAll() // or .IncludeNonLazy
            .Region("regionName")
            .NonStrictReadWrite();
    

    不会使用第二级缓存(类/实例级别)。这很棒 - 因为我们手中有如何设置它。

    流畅的NHibernate - 将其作为惯例应用

    Q & A讨论如何在全球范围内应用(事实上不适用)这些设置 - 通过特殊的Fluent NHibernate功能 - Convention (在我的情况下,这个Q&amp; ;除了这个问题,建议使用A)

    NHibernate second level cache caching entities with no caching configuration

    小代码段引用:

    public class ClassConvention : IClassConvention
    {
        public void Apply(IClassInstance instance)
        {
            instance.Table(instance.EntityType.Name);
            instance.LazyLoad();
            instance.Cache.NonStrictReadWrite();
        }
    }
    

    最后,我们应该在这里提到4.选项(查询级别.SetCacheable(true)应该与第二级缓存一起使用:

    19.4. The Query Cache

      

    也可以缓存查询结果集。这仅适用于使用相同参数频繁运行的查询。要使用查询缓存,您必须先启用它:

    <add key="hibernate.cache.use_query_cache" value="true" />
    
      

    ...请注意,查询缓存不会缓存结果集中任何实体的状态;它仅缓存标识符值和值类型的结果。 因此,查询缓存应始终与二级缓存一起使用 ...

    摘要:使用NHibernate进行缓存非常强大。原因是1)因为它是可插入的(请参阅我们可以开箱即用的提供商数量,例如here)和2)它是可配置的。事实上,我们可以使用不同的并发策略,区域,延迟加载处理......甚至 NOT 来使用它们......是必不可少的。有些实体几乎只读了#34; (例如国家代码清单),而有些则变化很大......

    我们所能做的最好的就是玩。去尝试。最后,我们可以拥有性能良好的油井机

答案 2 :(得分:-2)

是的,您必须为所有实体执行此操作。但是,这可以通过xml配置而不是映射来完成: Configuring NHibernate second level caching in an MVC app