ASP.NET缓存管理的几种方法

尽管缓存管理在Windows应用程序中已经不再是个问题,但在web环境下依然是个挑战。因为HTTP是一个无状态的协议并且web服务无法识别不同请求的用户。识别不同的请求究竟是哪个特定用户发出的,并且存储这些信息以便它在以后请求中能被重新使用,对我们来说非常重要。ASP.NET提供了很多特性用来在客户端和服务器端存储这些数据,但是有时我们会对“我们什么时候使用它们(哪个)”感到疑惑。在ASP.NET中,我们会遇到像Session,Application以及Cache这些对象,为了有效地在web应用中有效地使用它们,理解他们之间的不同对我们来说非常重要。

背景

在这篇文章中,我将谈到在ASP.NET中不同的缓存管理方法。在web应用中,有时需要在服务端存储数据以避免从数据库检索数据和数据格式化逻辑所需的开销来提高性能,同时在接下来的请求中我们可以跨用户、跨应用、跨机器地重用同样的数据。所以,为了实现这个目的我们需要在服务端缓存数据。

缓存帮我们在3个方面实现了提高服务质量

•性能(Performance)-通过减少检索数据和格式化操作开销,缓存提高了应用程序的性能。
•可伸缩性(Scalability)-由于缓存减少了检索数据和格式化操作的开销,它降低了服务端的负载,因而提高了应用程序的可伸缩性。
•可用性(Availability)-由于应用程序从缓存中读取数据,应用程序可以在其它系统或数据库连接失败时继续运行。
不同方法

在web应用中,我们可以在服务端和客户端缓存数据、页面等。我们分别来看一下在服务端和客户端缓存。

服务端缓存管理

ASP.NET Session state

Session用来缓存每个用户的信息。这意味着这些数据是不能跨用户共享的,它只限定了创建这个会话(Session)的用户来使用它。ASP.NET中Session就是用来区分用户的。

Session能用三种方式来托管:

•进程内(Inproc)-会话状态存储在aspnet_wp.exe进程中。当应用程序域回收时Session数据会丢失。
•状态服务器(StateServer)-会话状态存储在不同的进程内,可以在不同的机器上。因为它可以存储在不同的机器上,所以这个选项支持网站群。
•Sql数据库(SQLServer)-会话状态存储在SqlServer数据库中,这个选项也支持网站群。
对于状态服务器和Sql数据库来说,这两者都需要对缓存的对象进行序列化,因为要缓存的数据是要缓存到应用程序进程之外的。这两种方式都会影响性能因为数据检索与存储需要话费更多时间相对进程内缓存来说。所以要根据具体需要以确定使用哪种缓存方式。

下面示例代码展示了如何使用Session

代码如下:

string empNum = Request.QueryString["empnum"];
if (empNum != null)
{
    string details = null;

if (Session["EMP_DETAILS"] == null)
    {
        //Get Employee Details for employee number passed
        details = GetEmployeeDetails(Convert.ToInt32(empNum));

Session["EMP_DETAILS"] = details;
    }
    else
    {
        details = Session["EMP_DETAILS"];
    }

//send it to the browser
    Response.Write(details);
}

ASP.NET application object

ASP.NET提供了一个叫Application的对象用来存储所有用户都可以访问的数据。这个对象的生命周期与应用程序的生命周期一样,当应用程序启动时这个对象会被重新创建。与Session对象不同,Application对象可以被所有用户请求,因为这个对象是在应用程序域中创建和管理的,因而它也是不能在Web网站群中使用的。Application对象非常适合存储应用程序元数据(Config file data),这种数据可以被装载到Application对象中并且在整个应用程序周期中每个用户请求都可以访问其中的对象而不用重新装载。但是如果有这样的需求:在应用程序运行中无论什么时候对Config文件做了修改缓存数据必需失效,这时Application方式就不能提供这样的支持了。在这种情况下,就要考虑cache对象了,下面介绍cache对象的使用。

ASP.NET cache object

ASP.NET cache object是我最喜欢的缓存机制,这是为什么我在这里要多说一些的原因。ASP.NET提供了一个键-值对(key-value pair)对象--cache对象,它可以在system.web.caching名称空间中得到。它的范围是应用程序域,生命周期和应用程序生命周期一致。与Session对象不同,它是可以被不同用户来访问的。

尽管Application和Cache对象非常相似,主要区别在于Cache对象有拥有更多的特性,像过期策略、缓存依赖。它意味着数据存储在缓存对象可以根据预定义时间或它依赖的实体变化时过期或清楚,而这个特性Application对象是不支持的。

让我们来讨论下它支持的过期策略和缓存的依赖吧。

依赖

依赖意味着缓存的对象会被清除当依赖的实体发生变化时。所以可以定义一个依赖关系当依赖的对象发生变化时清除对应缓存对象。ASP.NET支持了两种依赖对象。

•文件依赖(File Dependency)-它提供了这样一种机制,当磁盘文件无论何时发生变化时自动清除缓存对象。举例来说,我的应用程序使用XML存储错误信息(错误号和错误消息的映射),用错误号来检索错误消息。每次当我想读取错误消息的时候,我不是每次都从磁盘去读取,而是当应用启动的时候将其放到Cache缓存里以便以后检索的时候再用。在程序运行过程中,当我添加新的错误信息或者修改已有的错误信息时,会发生什么情况呢?我需要停止程序运行去修改这些信息吗?根本不用,当做这样修改的时候,Cache缓存中的数据会自动失效,这就是文件缓存依赖。
下面例子显示了如何使用文件缓存来使Cache缓存失效的。所以,无论任何时候对error.xml文件作出修改时,缓存条目都会自动失效。

代码如下:

object errorData;
 //Load errorData from errors.xml
 CacheDependency fileDependency =
     new CacheDependency(Server.MapPath("errors.xml"));
 Cache.Insert("ERROR_INFO", errorData, fileDependency);

•键依赖(Key Dependency)-键依赖和文件依赖非常相似,唯一的区别在于它不是依赖文件而是依赖其它条目,当Cache依赖的条目发生改变时或被删除时,缓存会自动失效。这种方法对相互依赖的对象增加到缓存中,而且当主对象发生变化时这些相互依赖的缓存对象都要被释放的情况下很有用。例如,员工号、姓名、薪水同时增加到了缓存当中,如果员工号发生了改变或被删除,所有缓存中的员工信息都会被清除。在这个例子中,员工号在员工信息中充当依赖项。
下面例子显示了如何使用键依赖来使缓存失效的。

代码如下:

string[] relatedKeys = new string[1];
relatedKeys[0] = "EMP_NUM";
CacheDependency keyDependency = new CacheDependency(null, relatedKeys);
Cache["EMP_NUM"] = 5435;
Cache.Insert("EMP_NAME", "Shubhabrata", keyDependency);
Cache.Insert("EMP_ADDR", "Bhubaneswar", keyDependency);
Cache.Insert("EMP_SAL", "5555USD", keyDependency);

过期策略(Expiration Policy)

过期策略定义了如何以及何时让缓存的对象过期的。

•基于时间的过期(Time based expiration)-基于时间的过期提供了让用户为缓存对象预定义过期的时间。这个预定义时间可以是一个绝对时间如到2005年10月31号12点,或者相对时间,相对于缓存对象的存入时间。


代码如下:

//Absolute Expiration
Cache.Insert("EMP_NAME", "Shubhabrata", null,
             DateTime.Now.AddDays(1), Cache.NoSlidingExpiration);

//Sliding Expiration
Cache.Insert("EMP_NAME", "Shubhabrata", null,
             Cache.NoAbsoluteExpiration, TimeSpan.FromSeconds(60));

怎样知道一个缓存对象被清除了?

上面的例子描述了如何清除缓存对象,但有时我们需要知道什么时候对象从缓存中清除。可以,我们通过使用回调来实现。在上面错误信息的例子中,无论任何时候error.xml发生变化时,缓存的对象就会被清除。假设我们想要更新缓存与最新的错误消息。何时从缓存中清除对象,我们可以使用回调(Callback)来做进一步处理(重新加载对象到缓存中)。

下面例子显示了如何在缓存过期时使用回调的场景。

代码如下:

private void AddItemsToCache()
{   
    int empNum = 5435;
    CacheItemRemovedCallback onEmpDetailsRemove =
                    new CacheItemRemovedCallback(EmpDetailsRemoved);
    Cache.Insert("EMP_NUM", empNum, null,
                              Cache.NoAbsoluteExpiration,
                              Cache.NoSlidingExpiration,
                              CacheItemPriority.Default,
                              onEmpDetailsRemove);
}

private void EmpDetailsRemoved(string key, object val,
                              CacheItemRemovedReason reason)
{
    //When the item is expired
    if (reason == CacheItemRemovedReason.Expired)
    {
        //Again add it to the Cache
        AddItemsToCache();<BR>    }
}

在上面的例子中,你必须注意CacheItemPriority这个参数,它和Callback参数一起使用。CacheItemPriority用来设置增加到缓存中的对象的优先级。这个优先权告诉Cache当内存一旦很低时,这个优先级会指示对象的释放顺序。这个过程被称为清除(scavenging)。

.NET Remoting

你也许会想.NET remoting如何用于数据缓存?当我第一次听到这个问题时,这个问题就进到了我的脑海中。正如你所知道的.NET Remoting通过单例把对象共享给各个客户端,所以使用单例的对象可以用来缓存数据以共享数据给各个不同的客户端。因为.NET Remoting可以运行在进程和机器之外,当我们想要缓存对象并且跨服务、跨用户、尤其是用在网站群时,这个特性非常有用。这种方法我们可以将数据缓存到单例对象的数据成员里并且提供方法去读取和存储数据。当我们实现这种方法时,我们必须确保缓存的remoting对象不被垃圾回收器清除了。因而我们必须设置Remoting对象的缓存永不过期以至永远不会超时。我们可以重写InitializeLifetimeService和MarshalByRefObject方法使它们返回Null。但是这样做的主要问题是性能,通过分析使用这种方法比其它方法的性能都差。不管怎样,应该由设计师或开发者根据具体需求选择出最合适的方法。

内存映射文件(Memory-Mapping files)

大家都知道内存映射文件是什么,它基于映射到物理磁盘上的文件到应用程序存储空间的一个特定的地址范围。这种方式允许不同的进程使用相同的数据从而增加应用程序的性能。因为使用内存映射文件在ASP.NET应用中并不流行,我个人也不建议使用这种方法因为它增加了程序的复杂性,并且.NET Framework也不支持这样。但是如果有人喜欢使用这种方法的话,他必须为他们的需求开发出自定义的解决方案。

静态变量(Static variables)

我们可以使用静态变量来存储全局的数据或对象,以便在整个应用程序生命周期来访问它。同样地,我们也可以使用静态对象来缓存数据,并且可以提供方法来从缓存中检索和存储数据。因为静态对象存储在进程中,性能非常快。但是用静态变量实现过期策略和缓存依赖是非常复杂的,我还是比较喜欢使用Cache相比用静态变量。另一个问题是用户自定义缓存对象必须是线程安全的,所以实现它必须特别小心。

自定义静态缓存可以用下面方法实现:

代码如下:

public class CustomCache
{
    //Synchronized to implement thread-safe
    static Hashtable _myCache =
             Hashtable.Synchronized(new Hashtable());

public static object GetData(object key)
    {
        return _myCache[key];
    }

public static void SetData(object key, object val)
    {
        _myCache[key] = val;
    }
}

数据库

我们可以使用数据库来存储数据来实现跨用户、跨机器的数据共享。当我们想要缓存非常大的数据对象时,这是一种非常好的方式。使用这种方式来存储小的数据是得不偿失的(性能低),用于存储少量数据可以寻找其它进程内的缓存机制。存储到数据库中的缓存数据需要经过序列化成XML来方便存储和检索,在.NET Framework中我们也可以使用其它类型的序列化格式。

页面输出缓存(ASP.NET page output caching)

有时,我们的web应用程序在一定的时间范围内对于某些页面来说是不会变化的,例如HR站点中,员工工资信息不会频繁地变动,它们在一个月一般只变动一次。一般来说都是在一个月的第一天发生变化。所以,对特定的员工来说,一个月中这个员工的页面内容是不会变化的。所以,把这些页面在服务器上缓存起来以避免每次请求重新计算的过程,这真是个不错的主意。为了达到这个目的,.NET为我们提供了在服务端指定特定时间缓存输出页面的特性;它也提供了缓存页面片段的特性。在这儿我不再详细去描述这种缓存方法了,因为网络上有很多关于这方面的详细介绍。这是一个非常长的部分如果我们现在讨论它,我计划在其它章节去讨论它。

代码如下:

<!-- VaryByParm - different versions of same page will be
cached based on the parameter sent through HTTP Get/Post
Location - where the page is cached -->
<%@OutputCache Duration="60" VaryByParam="empNum"
                                       Location="Server"%>

我们来对比一下我们所讨论的这些缓存:






































方法  是否支持网站群? 备注

ASP.NET Session State
- InProc
- StateSerer
- SQLServer

No
Yes
Yes

Unlike other option, it stores only user session specific data
ASP.NET Application Object No  
ASP.NET Cache Object No  
.NET Remoting Yes  
Memory-Mapped files No  
Static Variables No  
Database Yes  
ASP.NET Page Output Caching No  

客户端缓存管理

在上面章节中我们讨论了在服务端的不通缓存方式,但有时我们希望能在客户端缓存数据和页面以提高性能。使用客户端缓存可以降低服务端的负载压力,但这种缓存机制却存在安全问题因为数据是存储在客户端。在客户端缓存也有不同的方式,我将简单地谈到几种。

Cookies

Cookie对web开发人员中是非常熟悉的概念,Cookie存储在客户端,当客户端每次发送请求时都会将它发送到服务端,服务端响应时也会把它发回到客户端。因为它限制了字节数(4096个字节),所以它只能缓存比较小的数据。它可以使用过期策略使它在一段特定的时间之后失效。下面的例子显示了在ASP.NET中如何使用Cookie。

代码如下:

if (this.Request.Cookies["MY_NAME"] == null)
{
    this.Response.Cookies.Add(new HttpCookie("MY_NAME",
                                       "Shubhabrata Mohanty"));
}
else
{
    this.Response.Write(this.Request.Cookies["MY_NAME"].Value);
}

ViewState

.NET ViewState是一个新的概念。和页面相关的数据和控件都是存储在ViewState,这些保留值可以跨多个请求道服务器。如果你还记得,在VB-ASP应用开发中跨多个请求存储数据是通过Hidden控件的。事实上ViewState在ASP.NET是隐藏控件的内部实现,但对比隐藏控件它做了散列化以增加安全性。去看ViewState是如何实现的,你可以打开页面查看源代码。ViewState也不能存储大量数据因为它每个请求都会发送到服务端。

代码如下:

protected void Page_Load(object sender, EventArgs e)
{
    if (this.ViewState["MY_NAME"] == null)
    {
        this.ViewState["MY_NAME"] = "Shubhabrata Mohanty";
    }

//txtName is a TextBox control
    this.txtName.Text = this.ViewState["MY_NAME"].ToString();
}

Hidden fields

Hidden field在VB-ASP Web开发中非常流行。Hidden fields和其它控件的使用非常相似,但它在输出页面上是看不到的。和ViewState一样它也不能存储大量数据。注:隐藏框架(Hidden frames)可以在客户端缓存数据,但不是所有浏览器都支持隐藏框架。

代码如下:

<!--In ASP.NET-->
<asp:HiddenField ID="myHiddenField" Value="Shubhabrata"
                                             runat="server" />
<!--In HTML-->
<input id="myHiddenField" type="hidden" value="Shubhabrata" />

微软IE浏览器缓存

因为我们是在谈论微软的ASP.NET,为什么不讨论一下微软的另外一种缓存能力呢?微软的IE浏览器提供了另一种机制在客户端缓存页面,这可以使用EXPIRES设置指令添加到HTML页面或在IIS中手动设置。到IIS中的HTTP标签属性窗口,然后选择使内容过期复选框。我们可以使用这个设置在客户端缓存静态网页和图片。

时间: 2012-12-03

.NET 缓存模块设计实践

上一篇谈了我对缓存的概念,框架上的理解和看法,这篇承接上篇讲讲我自己的缓存模块设计实践. 基本的缓存模块设计 最基础的缓存模块一定有一个统一的CacheHelper,如下: public interface ICacheHelper { T Get<T>(string key); void Set<T>(string key, T value); void Remove(string key); } 然后业务层是这样调用的 public User Get(int id) { if

ASP.NET性能优化之让浏览器缓存动态网页的方法

OutputCache是针对所有访问服务器资源的用户,本篇要介绍的浏览器缓存则是针对单个用户,让浏览器在我们的控制下彻底不持续访问服务器上的动态内容,也就是我们要让浏览器变成我们的缓存机制中的一部分,在某些特定的场景下最大化地提升ASP.NET站点的性能.如果说OutputCache是从广度上提升并发效率,则浏览器缓存是从深度上提升效率. 一:HTTP头简介 1.1浏览器第一次请求 假设我们请求一个URL地址,譬如我服务器上的一个静态页面http://192.168.0.77/luminji2/

asp.net 客户端浏览器缓存的Http头介绍

让浏览器做缓存需要给浏览器发送指定的Http头,告诉浏览器缓存多长时间,或者坚决不要缓存.作为.net的程序员,其实我们一直都在用这种方法,在OutputCache指令中指定缓存的Location为Client时,其实就是给浏览器发送了一个Http头,告诉浏览器这个Url要缓存多长时间,最后修改的时间. 微软在OutputCacheModule中对这些缓存用到的Http头给我们进行了很好的封装,但是了解这些Http头可以更灵活的使用它们. 和客户端缓存相关的Http头有以下几个,分别是: 1.

.net/c# memcached缓存获取所有缓存键的方法步骤

使用组件 memcached 1.2.6 .net 类库 memcacheddotnet_clientlib-1.1.5 1.增加memcacheddotnet_clientlib-1.1.5代码 下载好组件后,用vs打开.net类库memcacheddotnet_clientlib-1.1.5,打开MemCachedClient.cs,增加如下方法: 复制代码 代码如下: public Hashtable Stats(ArrayList servers, string command)    

ASP.NET页面在IE缓存的清除办法

以前没有注意到这个问题,今天在看Blog的时候发现有篇文章不错,或许以后值得借鉴: 一般情况下,WEB页面都会在Internet临时文件夹中有一个临时文件.我在操作IE时会出现这种情况:假如说有一个页面Page1.aspx,在我第一次访问后,它会在我的Internet临时文件夹里有一个Page1.aspx文件生成.之后,如果我改了Page1.aspx的数据后再次访问该页面,发现IE并没有对这个Page1.aspx的数据进行更新,反而打开的是我第一次访问的页面.为什么会这样,是因为IE自动(默认)

ASP.net Substitution 页面缓存而部分不缓存的实现方法

文件1:Deafault.aspx 复制代码 代码如下: <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %> <%@ outputcache duration="10" varybyparam="none" %> <!-

Asp.Net Cache缓存使用代码

复制代码 代码如下: public DataSet createCache() { //返回DataSet DataSet ds=new DataSet(); OleDbConnection conn=new OleDbConnection("provider=microsoft.jet.oledb.4.0;data source="+Server.MapPath("data.mdb")); conn.Open(); string sql="select

asp.net 提高网站速度及如何利用缓存

输出缓存和片段缓存的优点是非常易于实现,在大多数情况下,使用这两种缓存就足够了.而缓存API则提供了额外的灵活性(实际上是相当大的灵活性),可用于在应用程序的每一层利用缓存.本文全面介绍了这三种缓存技术在系统各层中的应用. 在ASP.NET提供的许多特性中,缓存支持无疑是我最欣赏的特性,我这样说当然是有充分理由的.相比ASP.NET的所有其他特性,缓存对应用程序的性能具有最大的潜在影响,利用缓存和其他机制,ASP.NET开发人员可以接受使用开销很大的控件(例如,DataGrid)构建站点时的额外

ASP.NET网站管理系统退出 清除浏览器缓存,Session的代码

1.在系统登陆成功时记录登陆的用户名.密码等信息(登陆功能的部分代码) 复制代码 代码如下: Session["id"] = user.id.ToString(); Session["name"] = user.name.ToString(); Session["pwd"] = user.password.ToString(); Session["time"] = user.LoginTime.ToString(); Sess

asp.net(C#)遍历memcached缓存对象

STATS命令 遍历memcached缓存对象(C#)转载之青草堂 出于性能考虑,memcached没有提供遍历功能,不过我们可以通过以下两个stats命令得到所有的缓存对象. 1.stats items 显示各个slab中item的数目. 2.stats cachedump slab_id limit_num 显示某个slab中的前limit_num个key列表,显示格式:ITEM key_name [ value_length b; expire_time|access_time s] 除了

充分利用ASP.NET的三种缓存提高站点性能的注意方法

ASP.NET提供三种主要形式的缓存:页面级输出缓存.用户控件级输出缓存(或称为片段缓存)和缓存API. 尽早缓存:经常缓存  您应该在应用程序的每一层都实现缓存.向数据层.业务逻辑层.UI或输出层添加缓存支持.内存现在非常便宜-因此,通过以智能的方式在整个应用程序中实现缓存,可以获得很大的性能提高. 页面级输出缓存 最简单的缓存形式,只是在内存中保留为响应请求而发送的HTML的副本. 要实现页面输出缓存,只要将一条OutputCache指令添加到页面即可. <%@ OutputCache Du

Laravel中扩展Memcached缓存驱动实现使用阿里云OCS缓存

Laravel是我最近用得非常多而且越用就越喜欢的一款PHP框架,由于没有向下兼容的历史包袱,完全面向对象的风格,借助 Facades 优雅的IoC Container 实现,采用 Composer进行包管理,可以方便地引入和使用开源社区里的优秀组件--总而言之,这是一款真正让你能够 "code happy" 的"巨匠级PHP开发框架". 在尝试把自己的 Laravel App 部署到阿里云的时候,遇到了一个问题: Laravel 支持 Memcached 缓存,阿

Memcached缓存系统的介绍、安装以及应用方法详解

本文实例讲述了Memcached缓存系统的介绍.安装以及应用方法.分享给大家供大家参考,具体如下: 一. memcached 是什么? memcached is a high-performance, distributed memory object caching system, generic in nature, but intended for use in speeding up dynamic web applications by alleviating database loa

ASP.NET使用Ajax返回Json对象的方法

一.新建一个html页面,如注册页面"Register.htm" <!DOCTYPE html> <html > <head> <title>用户注册</title> <meta charset="utf-8" /> <style type="text/css"> .msg { color:Red; } </style> </head> &

Laravel使用memcached缓存对文章增删改查进行优化的方法

本文实例讲述了Laravel使用memcached缓存对文章增删改查进行优化的方法.分享给大家供大家参考,具体如下: 这里我们将以文章的增删改查作为实例系统讲述缓存的使用,这个实例是对之前创建RESTFul风格控制器实现文章增删改查这篇教程的改造和升级,我们将在其基础上融合进Eloquent ORM和模型事件,将应用的场景直接拉到生成环境. 1.准备工作 路由及控制器 路由的定义和控制器的创建保持和创建RESTFul风格控制器实现文章增删改查中一样. 创建数据表 关于文章对应数据表我们在数据库部

jquery遍历筛选数组的几种方法和遍历解析json对象

jquery grep()筛选遍历数组 复制代码 代码如下: $().ready( function(){ var array = [1,2,3,4,5,6,7,8,9]; var filterarray = $.grep(array,function(value){ return value > 5;//筛选出大于5的 }); for(var i=0;i<filterarray.length;i++){ alert(filterarray[i]); } for (key in filtera

JS遍历页面所有对象属性及实现方法

for...in循环的Javascript示例: <html> <head> <title>一个使用到for...in循环的Javascript示例</title> </head> <body> <script type="text/javascript"> // 创建一个对象 myObject 以及三个属性 sitename, siteurl, sitecontent. var myObject =

JS遍历数组和对象的区别及递归遍历对象、数组、属性的方法详解

废话不多说了,直奔主题,你,具体代码如下所示: <script> //----------------for用来遍历数组对象-- var i,myArr = [1,2,3]; for (var i = 0; i < myArr.length; i++) { console.log(i+":"+myArr[i]); }; //---------for-in 用来遍历非数组对象 var man ={hands:2,legs:2,heads:1}; //为所有的对象添加cl

Yii配置与使用memcached缓存的方法

本文实例讲述了Yii配置与使用memcached缓存的方法.分享给大家供大家参考,具体如下: 1. 下载memcached软件包,解压,把memcached.exe 放到随意一个地方,比如:d:/memcached/ 下. 2. 开始->运行->输入cmd,命令行打开memcached.exe,所在文件夹,输入:memcached.exe -d install  安装 3. 输入memcached.exe -d start 启动 4. 中加入 extension=php_memcache.dl