【问题标题】:What is the difference between a nhibernate query cache and entity cache when using second level caching?使用二级缓存时,休眠查询缓存和实体缓存有什么区别?
【发布时间】:2014-10-28 05:04:08
【问题描述】:

我正在尝试设置 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 profiler 时,我看到了二级缓存,我没有设置这个 Cache.ReadOnly() 值。

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

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

  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 分析器时看到了一些二级缓存命中,但我没有在实体映射代码中设置缓存。我正在尝试确定是否需要做其他事情才能使缓存正常工作(或更好地工作)

当我使用 nhibernate 分析器(没有在实体级别设置 Cache.ReadWrite() )时,它似乎仍然会命中二级缓存。 (见下面的截图)

【问题讨论】:

    标签: caching nhibernate fluent-nhibernate


    【解决方案1】:

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

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

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

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

    【讨论】:

      【解决方案2】:

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

      1. &lt;property name="cache.use_second_level_cache"&gt; - 全局切换
      2. &lt;property name="cache.use_query_cache"&gt; - 全局切换
      3. &lt;cache usage="read-write" region="xxx"/&gt; - 类/实例级别
      4. .SetCacheable(true).SetCacheMode(CacheMode.Normal).SetCacheRegion("yyy") - 每个查询

      前两个是全局启用/禁用。必须先打开它们,然后我们才能使用下一个/最后两个设置。

      但事实上,已经有了答案。全局手段 - 支持缓存,本地手段 - 决定 1) 如何 2) 哪个 类/查询被处理 - 如果会或 全部。

      例如,这是SessionFactory.cs的一个sn-p:

      ...
      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. 设置 (&lt;cache usage="read-write" region="xxx"/&gt;) 不重要...所有映射的实例都将被缓存...非托管,默认方式...

      幸运的是,这不是真的。 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();
      

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

      Fluent NHibernate - 将其作为惯例应用

      有一个Q & A 讨论如何在全球范围内应用这些设置(实际上不适用) - 通过特殊的 Fluent NHibernate 功能 - 约定 (在我的情况下,这个问答是建议抛开这个问题)

      NHibernate second level cache caching entities with no caching configuration

      小代码sn -p 引用:

      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 来使用它们......是必不可少的。有些实体是“几乎只读的”(例如国家代码列表),而有些则在高度变化......

      我们能做的最好的就是玩。去体验。最后,我们可以拥有上油的机器,性能良好。

      【讨论】:

        【解决方案3】:

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

        【讨论】:

        • 我看不到您在哪里通过 XML 配置进行设置。我的问题是“要使用二级缓存,我应该把 Cache.ReadWrite 放在我的每个实体映射中吗?
        • 我有一些例子,我看到它在没有实体缓存的情况下达到了二级缓存,所以如果你说我需要使用二级缓存来工作,我很困惑它是如何工作的。 . .
        • 我给了你一个明确的答案“是的,你必须为所有实体这样做”
        猜你喜欢
        • 2010-11-26
        • 2010-10-20
        • 2015-10-23
        • 1970-01-01
        • 2014-01-12
        • 2018-05-22
        • 2016-04-27
        • 2013-03-18
        相关资源
        最近更新 更多