System.Runtime.Caching.MemoryCache vs HttpRuntime.Cache - 有啥区别吗?

Posted

技术标签:

【中文标题】System.Runtime.Caching.MemoryCache vs HttpRuntime.Cache - 有啥区别吗?【英文标题】:System.Runtime.Caching.MemoryCache vs HttpRuntime.Cache - are there any differences?System.Runtime.Caching.MemoryCache vs HttpRuntime.Cache - 有什么区别吗? 【发布时间】:2012-11-22 03:26:48 【问题描述】:

我想知道MemoryCacheHttpRuntime.Cache之间有什么区别,ASP.NET MVC项目中首选哪一个?

据我了解,两者都是线程安全的,API 乍一看或多或少相同,那么什么时候使用有什么区别?

【问题讨论】:

【参考方案1】:

HttpRuntime.Cache 获取当前应用程序的Cache

MemoryCache 类类似于 ASP.NET 的 Cache 类。

MemoryCache 类具有许多用于访问缓存的属性和方法,如果您使用过 ASP.NET Cache 类,您会很熟悉。

HttpRuntime.CacheMemoryCache 之间的主要区别在于后者已被更改,以使其可供非 ASP.NET 应用程序的 .NET Framework 应用程序使用。

补充阅读:

Justin Mathew Blog - Caching in .Net 4.0 Jon Davis Blog - Four Methods Of Simple Caching In .NET

更新:

根据用户反馈,Jon davis 博客有时无法正常工作。因此,我将整篇文章作为图像。请参阅。

注意:如果不清楚,只需点击图片即可。之后它将在浏览器中打开。然后再次点击放大:)

【讨论】:

John Davis 的那篇文章读起来真的很不错 - 一个地方清楚利弊。 绝对一切都在一个地方。好一个。此外,Davis 还提到了 4 种不同的缓存方法。 @Spikeh 对我来说加载正常。 @Stijn 谢谢,前几天没有加载,但现在又回来了 :) @sampath 现在可以使用了。昨天看起来好像该网站被黑客入侵了。感谢您的帮助!【参考方案2】:

这是 Jon Davis 的文章。为了保持可读性,我删除了现已过时的 EntLib 部分、介绍和结论。


ASP.NET 缓存

ASP.NET 或 System.Web.dll 程序集确实具有缓存机制。它从未打算在 Web 上下文之外使用,但它可以在 Web 之外使用,并且它确实在各种哈希表中执行所有上述过期行为。

在搜索了 Google 之后,似乎很多讨论过 .NET 中内置缓存功能的人都在他们的非 Web 项目中使用了 ASP.NET 缓存。这不再是 .NET 中最可用、最受支持的内置缓存系统; .NET 4 有一个 ObjectCache,我稍后会介绍。 Microsoft 一直坚持认为 ASP.NET 缓存不适合在 Web 之外使用。但是很多人仍然停留在 .NET 2.0 和 .NET 3.5 中,需要一些东西来工作,而这恰好对很多人有用,尽管 MSDN 说得很清楚:

注意:Cache 类不适合在 ASP.NET 应用程序之外使用。它是为在 ASP.NET 中使用而设计和测试的,以便为 Web 应用程序提供缓存。在其他类型的应用程序中,例如控制台应用程序或 Windows 窗体应用程序,ASP.NET 缓存可能无法正常工作。

ASP.NET 缓存的类是 System.Web.dll 中的 System.Web.Caching.Cache。但是,您不能简单地新建一个 Cache 对象。您必须从 System.Web.HttpRuntime.Cache 获取它。

Cache cache = System.Web.HttpRuntime.Cache;

在 MSDN here 上记录了如何使用 ASP.NET 缓存。

优点:

    它是内置的。 尽管使用 .NET 1.0 语法,但使用起来相当简单。 在网络环境中使用时,它经过了良好测试。在 Web 环境之外,根据 Google 搜索,尽管 Microsoft 不建议这样做,但只要您使用的是 .NET 2.0 或更高版本,它通常不会引起问题。 当项目被删除时,您可以通过委托通知,如果您需要保持它处于活动状态并且您无法提前设置项目的优先级,这是必要的。 单个项目具有灵活性,可在本文顶部的移除方法列表中选择 (a)、(b) 或 (c) 中的任何一种到期和移除方法。您还可以将过期行为与物理文件的存在关联起来。

缺点:

    它不仅是静态的,而且只有一个。您不能使用自己的静态缓存实例创建自己的类型。您的整个应用程序只能有一个存储桶,期间。您可以使用自己的包装器来包装存储桶,这些包装器可以在键中预注入前缀,并在您拉回键/值对时删除这些前缀。但仍然只有一个桶。一切都集中在一起。例如,如果您有一个服务需要分别缓存三种或四种不同类型的数据,这可能会很麻烦。对于可怜的简单项目来说,这应该不是什么大问题。但是,如果项目由于其要求而具有任何显着程度的复杂性,那么 ASP.NET 缓存通常就不够用了。 物品可能会消失,不管是哪一种。很多人都没有意识到这一点——我没有,直到我刷新了关于这个缓存实现的知识。默认情况下,ASP.NET 缓存旨在在“感觉”喜欢时销毁项目。更具体地说,请参阅本文顶部我对缓存表的定义中的 (c)。如果同一进程中的另一个线程正在处理完全不同的事情,并且它将高优先级项目转储到缓存中,那么一旦 .NET 决定它需要一些内存,它就会开始销毁缓存中的一些项目。他们的优先级,优先级较低。此处记录的所有用于添加缓存项的示例都使用默认优先级,而不是 NotRemovable 优先级值,该值可防止出于内存清除目的而将其删除,但仍会根据过期策略将其删除。在缓存调用中使用 CacheItemPriority.NotRemovable 可能很麻烦,否则需要一个包装器。 键必须是字符串。例如,如果您正在缓存数据记录,其中记录的键是长整数或整数,则必须先将键转换为字符串。 语法陈旧。它是 .NET 1.0 的语法,甚至比 ArrayList 或 Hashtable 还要丑陋。这里没有泛型,没有 IDictionary 接口。它没有 Contains() 方法,没有 Keys 集合,没有标准事件;它只有一个 Get() 方法和一个与 Get() 做同样事情的索引器,如果没有匹配则返回 null,再加上 Add()、Insert()(冗余?)、Remove() 和 GetEnumerator() . 忽略设置默认过期/删除行为的 DRY 原则,以便您忘记它们。每次添加添加项时,您都必须明确告诉缓存您希望添加的项如何过期或删除。 无法访问缓存项的缓存详细信息,例如添加时的时间戳。此处的封装有点过火,当您在代码中尝试确定缓存项是否应针对另一种缓存机制(即会话集合)无效时,很难使用缓存。 删除事件不会作为事件公开,必须在添加时进行跟踪。 如果我说得还不够,Microsoft explicitly recommends against it 在网络之外。如果您在网络之外被 .NET 1.1, you not supposed to use it with any confidence of stability at all 诅咒,请不要打扰。

.NET 4.0 的 ObjectCache / MemoryCache

Microsoft 最终在最新版本的 .NET Framework 中实现了一个抽象 ObjectCache 类,以及一个 MemoryCache 实现,该实现继承并实现了 ObjectCache 以在非 Web 设置中用于内存中的目的。

System.Runtime.Caching.ObjectCache 在 System.Runtime.Caching.dll 程序集中。它是一个抽象类,它声明了与 ASP.NET 缓存中的基本相同的 .NET 1.0 样式接口。 System.Runtime.Caching.MemoryCache 是 ObjectCache 的内存实现,与 ASP.NET 缓存非常相似,只是有一些变化。

要添加具有滑动到期的项目,您的代码应如下所示:

var config = new NameValueCollection();  
var cache = new MemoryCache("myMemCache", config);  
cache.Add(new CacheItem("a", "b"),  
    new CacheItemPolicy  
      
        Priority = CacheItemPriority.NotRemovable,  
        SlidingExpiration=TimeSpan.FromMinutes(30)  
    ); 

优点:

    它是内置的,现在由 Microsoft 在网络之外提供支持和推荐。

    与 ASP.NET 缓存不同,您可以实例化 MemoryCache 对象实例。

    注意:它不一定是静态的,但应该是——即Microsoft’s recommendation (see yellow Caution)。

    与 ASP.NET 缓存的接口相比,已进行了一些细微的改进,例如在添加项目时订阅删除事件的能力,删除多余的 Insert(),项目可以添加一个带有定义缓存策略的初始化器的 CacheItem 对象,并添加了 Contains()。

缺点:

    仍然没有完全强化 DRY。根据我的少量经验,您仍然无法设置一次滑动过期时间跨度而忘记它。坦率地说,虽然上面 item-add 示例中的策略更具可读性,但它需要非常冗长。 它仍然不是通用键控;它需要一个字符串作为键。因此,如果您正在缓存数据记录,则不能存储 as long 或 int,除非您转换为字符串。

DIY:自己打造一个

创建一个执行显式或滑动过期的缓存字典实际上非常简单。 (如果您希望出于清除内存的目的而自动删除项目,这将变得更加困难。)这就是您所要做的:

    创建一个名为 Expiring 或 Expirable 之类的值容器类,该类将包含一个 T 类型的值、一个 DateTime 类型的 TimeStamp 属性,用于存储该值何时添加到缓存中,以及一个 TimeSpan,用于指示距离项目应过期的时间戳。对于显式过期,您可以只公开一个属性设置器,该设置器在给定日期减去时间戳的情况下设置 TimeSpan。 创建一个类,我们称之为 ExpirableItemsDictionary,它实现了 IDictionary。我更喜欢让它成为一个由消费者定义的泛型类。 在#2 中创建的类中,添加一个 Dictionary> 作为属性并将其命名为 InnerDictionary。 #2 中创建的类中的 IDictionary 实现应该使用 InnerDictionary 来存储缓存项。封装将通过上面 #1 中创建的类型的实例隐藏缓存方法的详细信息。 确保索引器 (this[])、 ContainsKey() 等在返回值之前小心清除过期项目并移除过期项目。如果项目被删除,则在 getter 中返回 null。 对所有 getter、setter、ContainsKey() 使用线程锁,尤其是在清除过期项时。 只要项目因过期而被移除,就会引发事件。 添加一个 System.Threading.Timer 实例并在初始化期间对其进行装配,以每 15 秒自动删除过期项目。这与 ASP.NET 缓存的行为相同。 您可能希望添加一个 AddOrUpdate() 例程,该例程通过替换项目容器(Expiring 实例)上的时间戳(如果已存在)来推出滑动过期。

Microsoft 必须支持其原始设计,因为它的用户群已经建立了对它们的依赖,但这并不意味着它们是好的设计。

优点:

    您可以完全控制实施。 可以加强 DRY,方法是设置默认缓存行为,然后在每次添加项目时只需放入键/值对而不声明缓存详细信息。 可以实现现代接口,即IDictionary<K,T>。这使得它更容易使用,因为它的接口作为字典接口更容易预测,而且它更容易被使用 IDictionary 的帮助程序和扩展方法访问。 缓存细节可以不封装,例如通过公共只读属性公开您的 InnerDictionary,允许您针对缓存策略编写显式单元测试,并使用其他缓存策略扩展您的基本缓存实现以它为基础。 虽然对于那些已经熟悉 ASP.NET 缓存或缓存应用程序块的 .NET 1.0 样式语法的人来说,它不一定是一个熟悉的界面,但您可以定义界面来随心所欲。 可以使用任何类型的键。这是创建泛型的原因之一。并非所有内容都应使用字符串作为键。

缺点:

    不是微软发明的,也不是微软认可的,所以它不会有同样的质量保证。 假设仅实现了我上面描述的指令,不会“随意”清除优先级清除内存的项目(无论如何,这是缓存的一个极端实用功能.. 购买 RAM,你会使用缓存,RAM 很便宜)。

在所有这四个选项中,这是我的偏好。我已经实现了这个基本的缓存解决方案。到目前为止,它似乎工作得很好,没有已知的错误(如果有,请与下面的 cmets 或 jon-at-jondavis 联系我!!),我打算在我所有需要的较小的副项目中使用它基本缓存。这里是:

Github 链接:https://github.com/kroimon/ExpirableItemDictionary

旧链接:ExpirableItemDictionary.zip

值得一提:AppFabric、NoSQL、等

请注意,这篇博客文章的标题表示“简单缓存”,而不是“重型缓存”。如果您想涉足重型领域,您应该考虑专门的横向扩展解决方案。

【讨论】:

AppFabric is no longer supported by Microsoft, they now recommend Redis【参考方案3】:

如果您将经典的 ASP.NET MVC 应用程序迁移到 ASP.NET Core,MemoryCache.Default 也可以充当“桥梁”,因为 Core 中没有“System.Web.Caching”和“HttpRuntime”。

我还编写了一个小型基准测试来将 bool 项目存储 20000 次(以及另一个用于检索它的基准测试),并且 MemoryCache 似乎慢了两倍(27ms vs 13ms - 这是所有人的总数20k 次迭代),但它们都非常快,这可能会被忽略。

【讨论】:

【参考方案4】:

MemoryCache 就是它所说的,一个存储在内存

中的缓存

HttpRuntime.Cache(请参阅http://msdn.microsoft.com/en-us/library/system.web.httpruntime.cache(v=vs.100).aspx 和http://msdn.microsoft.com/en-us/library/system.web.caching.cache.aspx)在您在应用程序中配置的任何内容上都保持不变。

参见例如“ASP.NET 4.0:编写自定义输出缓存提供程序” http://weblogs.asp.net/gunnarpeipman/archive/2009/11/19/asp-net-4-0-writing-custom-output-cache-providers.aspx

【讨论】:

嗯,不知道第二个链接是不是误导,因为这里谈到了 OutputCache 和实现 OutputCacheProvider。 嗯,我找不到它会说,您可以使用不同的配置持久化 System.Web.Caching.Cache

以上是关于System.Runtime.Caching.MemoryCache vs HttpRuntime.Cache - 有啥区别吗?的主要内容,如果未能解决你的问题,请参考以下文章