6、数据库缓存依赖
更多的时候,我们的服务器性能损耗还是在查询数据库的时候,所以对数据库的缓存还是显得特别重要,上面几种方式都可以实现部分数据缓存功能。但问题是我们的数据有时候是在变化的,这样用户可能在缓存期间查询的数据就是老的数据,从而导致数据的不一致。那有没有办法做到,数据如果不变化,用户就一直从缓存中取数据,一旦数据变化,系统能自动更新缓存中的数据,从而让用户得到更好的用户体验。
答案是肯定的!.NET已经为我们提供了这样一种非常好的解决方法:SqlCacheDependency数据库缓存依赖。
实现步骤:
下面就让我们看一下如何实现数据库缓存依赖功能:
第一步: 修改web.config,让项目启用SqlCacheDependency 。
将下列代码加入web.config的<system.web>节:
<?xml version="1.0"?>
<configuration>
<appSettings/>
<connectionStrings>
<add name="NorthwindConn" connectionString="data source=.SQLEXPRESS;initial catalog=Northwind;user id=sa;password=deiqnygu" providerName="System.Data.SqlClient" />
</connectionStrings>
<system.web>
<caching>
<sqlCacheDependency enabled="true" pollTime="6000">
<databases>
<add name="Northwind" connectionStringName="NorthwindConn" />
</databases>
</sqlCacheDependency>
</caching>
</system.web>
</configuration>
这里的connectionStringName指定了在<connectionStrings>中添加的某一个连接字符串。name则是为该SqlCacheDependency起的名字,这个名字将在第3步中用到。
SqlCacheDependency类会自动完成对此配置节信息的读取以建立和数据库之间的联系。
注意:
在<databases>节的<add name="Northwind" connectionStringName="NorthwindConn" />中的name属性值必须和第三步的Page_Load代码中System.Web.Caching.SqlCacheDependency("Northwind", "Products"); 中的第一个参数(数据库名称)相一致。
第二步:执行下述命令,为 数据库启用缓存依赖。
打开Vsiual studio的命令提示窗口,输入:
aspnet_regsql.exe -S <Server> -U <Username> -P <Password> -ed -d <Database> -et -t <TableName>
如:aspnet_regsql.exe -S .msserver -U sa -P sa -ed -d Northwind -et -t products
参数-C后面的字符串是连接字符串(请替换成自己所需要的值),
参数-t后面的字符串是数据表的名字。
也可以按下面的方式
以下是该工具的命令参数说明:
-? 显示该工具的帮助功能;
-S 后接的参数为数据库服务器的名称或者IP地址;
-U 后接的参数为数据库的登陆用户名;
-P 后接的参数为数据库的登陆密码;
-E 使用当前登录用户的 Windows 集成认证进行身份验证。
-d 后接参数为对哪一个数据库采用SqlCacheDependency功能;
-C 连接数据库的连接字符串。如果您指定服务器(-S)和登录(-U和-P,或 -E)信息,则此选项不是必需的,因为连接字符串已经包含这些信息。
-t 后接参数为对哪一个表采用SqlCacheDependency功能;
-ed 允许对数据库使用SqlCacheDependency功能;
-dd 禁止对数据库采用SqlCacheDependency功能;
-et 允许对数据表采用SqlCacheDependency功能;
-dt 禁止对数据表采用SqlCacheDependency功能;
-lt 列出当前数据库中有哪些表已经采用sqlcachedependency功能。
第三步:在代码中使用缓存,并为其设置SqlCacheDependency依赖:
protected void Page_Load(object sender, EventArgs e)
{
string CacheKey = "cachetest";
object objModel = GetCache(CacheKey);//从缓存中获取
if (objModel == null)//缓存里没有
{
objModel = GetData();//把当前时间进行缓存
if (objModel != null)
{
//依赖数据库codematic中的P_Product表变化 来更新缓存
System.Web.Caching.SqlCacheDependency dep = new System.Web.Caching.SqlCacheDependency("Northwind", "Products");
SetCache(CacheKey, objModel, dep);//写入缓存
}
}
GridView1.DataSource = (DataSet)objModel;
GridView1.DataBind();
}
//查询数据
private DataSet GetData()
{
string conString = @"data source=.SQLEXPRESS;initial catalog=Northwind;user id=sa;password=deiqnygu";
string strSQL = "SELECT top 10 * FROM Products";
SqlConnection myConnection = new SqlConnection(conString);
DataSet ds = new DataSet();
myConnection.Open();
SqlDataAdapter adapter = new SqlDataAdapter(strSQL, myConnection);
adapter.Fill(ds, "Product");
myConnection.Close();
return ds;
}
/// <summary>
/// 获取当前应用程序指定CacheKey的Cache对象值
/// </summary>
/// <param name="CacheKey">索引键值</param>
/// <returns>返回缓存对象</returns>
public static object GetCache(string CacheKey)
{
System.Web.Caching.Cache objCache = HttpRuntime.Cache;
return objCache[CacheKey];
}
/// <summary>
/// 设置以缓存依赖的方式缓存数据
/// </summary>
/// <param name="CacheKey">索引键值</param>
/// <param name="objObject">缓存对象</param>
/// <param name="cacheDepen">依赖对象</param>
public static void SetCache(string CacheKey, object objObject, System.Web.Caching.CacheDependency dep)
{
System.Web.Caching.Cache objCache = HttpRuntime.Cache;
objCache.Insert(
CacheKey,
objObject,
dep,
System.Web.Caching.Cache.NoAbsoluteExpiration,//从不过期
System.Web.Caching.Cache.NoSlidingExpiration,//禁用可调过期
System.Web.Caching.CacheItemPriority.Default,
null);
}
从以上代码可以看出,和文件依赖基本相同,只是在存放缓存SetCache时存入的依赖对象不同罢了。这里用的是SqlCacheDependency。
其中,创建SqlCacheDependency的构造方法:
public SqlCacheDependency (string databaseEntryName,string tableName)
databaseEntryName :是在Web.config 文件的 caching 节的 sqlCacheDependency 的 databases 元素中定义的数据库的名称。
tableName :与 SqlCacheDependency 关联的数据库表的名称。
这样,只有当P_Product表的内容发生变化时,查询操作才会重新查询数据更新缓存的内容,可以大大减少数据库的重复查询和提高系统的性能和运行效率。
SQL Server 2005
SQL SERVER 2005内置支持SQL数据缓存依赖,内置通知传递服务,能够提供更小粒度的数据更改监测,使用和配置简单。
使用方法步骤:
检测是否已经启用Service Broker
Select DATABASEpRoPERTYEX('数据库名称','IsBrokerEnabled') -- 1 表示已经启用 0 表示没有启用
依据我的经验,如果直接在当前SqlServer2005上新建一个数据库的话,默认是打开的,如果是从其他地方数据库导过来的,导入之后默认关闭了。(可能有不准确,大家可以自己试验一下测试一下)。
启用Service Broker
ALTER DATABASE 数据库名称 SET ENABLE_BROKER;
在实现基于服务的SQL数据缓存依赖过程中,需要显式调用SqlDependency.Start来启动接受依赖项更改通知的侦听器。
SqlDependency.Start(connectionString); //推荐将这段代码加到Global.asax的Application_Start方法中,
SqlDependency.Stop(connectionString); //用于关闭,可加在Global.asax的Application_End方法中。
应用程序数据缓存中使用
SqlCommand cmd = new SqlCommand(sql,conn);
SqlCacheDependency scd = new SqlCacheDependency(cmd);
Cache.Insert(...,scd,...);
注意:
a). 必须设置完全限定名称的数据表。即表名前面需要加所有者,如dbo.test。
b). 必须明确设置所访问数据库列名称,不能使用“*”。
c). 必须保证不是聚合函数。如COUNT、MAX等。
CacheDependency、AggregateCacheDependency、SqlCacheDependency
(1) CacheDependency是AggregateCacheDependency和SqlCacheDependency的父类。主要用于在应用程序数据缓存对象与文件、缓存键、文件或缓存键的数组或另外一个CacheDependency对象之间建立依赖关系。CacheDependency监视依赖关系比便在任何对象更改时自动移除缓存对象。CacheDependency可以监测一组(到文件或目录的)文件路径的更改情况。
(2) AggregateCacheDependency主要用于实现聚合缓存依赖。如一笔数据同时对两个表进行缓存依赖,一旦其中任何一个表数据更改缓存将失效。
(3) SqlCacheDependency将应用程序数据缓存对象、页面输出缓存、数据源控件等与指定SQL Server数据库表或Sql Server 2005 查询结果之间建立缓存依赖关系,在表发生更改(Sql Server 2005 行级别更改)时,自动从缓存中删除和重新添加与该表关联的缓存对象。
一般而言:
SqlCacheDependency (SqlCommand) 用于SQL SERVER 2005
SqlCacheDependency (数据库名, 表名) 用于SQL SERVER 7.0/2000
7、第三方分布式缓存解决方案 Memcached和Cacheman
Memcached — 分布式缓存系统
1.Memcached是什么?
Memcached是高性能的,分布式的内存对象缓存系统,用于在动态应用中减少数据库负载,提升访问速度。Memcached通过在内存里维护一个统一的巨大的hash表,它能够用来存储各种格式的数据,包括图像、视频、文件以及数据库检索的结果等。Memcached由Danga Interactive最初为了加速 LiveJournal网站访问速度而开发的,后来被很多大型的网站采用。起初作者编写它可能是为了提高动态网页应用,为了减轻数据库检索的压力,来做的这个缓存系统。它的缓存是一种分布式的,也就是可以允许不同主机上的多个用户同时访问这个缓存系统,这种方法不仅解决了共享内存只能是单机的弊端, 同时也解决了数据库检索的压力,最大的优点是提高了访问获取数据的速度!基于memcached作者对分布式cache的理解和解决方案。memcached完全可以用到其他地方 比如分布式数据库,分布式计算等领域。Memcached将数据库负载大幅度降低,更好的分配资源,更快速访问。
2.Memcached工作机制
通过在内存中开辟一块区域来维持一个大的hash表来加快页面访问速度,和数据库是独立的。但是目前主要用来缓存数据库的数据。允许多个server通过网络形成一个大的hash,用户不必关心数据存放在哪,只调用相关接口就可。存放在内存的数据通过LRU算法进行淘汰出内存。同时可以通过删除和设置失效时间来淘汰存放在内存的数据。
现在一些.NET开发人员开始放弃ASP.NET内置的缓存机制,转而使用Memcached——一种分布式的内存缓存系统。当运行在单独的Web服务器上,你可以很容易地清除一个已经确认被改变了的缓存。可惜,ASP.NET没有一个很好的方法来支持多服务器。每个服务器上的缓存都对其他缓存的改变一无所知。
ASP.NET允许通过基于文件系统和数据库表的触发器来作废一个缓存。然而,这也存在问题,比如数据库触发器需要使用昂贵的轮询,以及触发器本身冗长的编程。但是,我们还是有其他的选择的。
不像ASP.NET内置的缓存机制,Memcached是一个分布式的缓存系统。任何Web服务器都能更新或删除一个缓存项,并且所有其他的服务器都能在下次访问这些缓存项的时候自动获取到更新的内容。这是通过把这些缓存项存储在一个或者多个缓存服务器上来实现的。每一个缓存项都根据它的关键字的哈希值来分配到一个服务器上。
表面看来,Memcached针对ASP.NET的API就像和内置的API一样。这让开发人员很容易地转换到Memcached上,仅仅通过在代码中查找和替换即可实现。
一个被推荐的解决方案是不根据缓存项的关键字来生成哈希键值。这将允许开发人员能够让一个给定页面中需要的所有缓存项,尽量存放在同一个服务器上。可惜,基于数据保存的地方而不是基于缓存项自身的关键字来生成哈希键,很容易产生错误,需要仔细来实现(这个算法)。
Memcached是基于Linux运行的,你可以在BSD的许可协议下使用Memcached。他也提供了针对C#的客户端以及Perl、Python、PHP、Java和其他语言的API:http://www.danga.com/memcached/apis.bml。还有一个Win32的移植版本(http://jehiah.cz/projects/memcached-win32/),可以让Memcached运行在非Linux的机器上。
Cacheman — .NET架构下的分布式缓存项目
Cacheman据说是由微软旗下的 Popfly 项目组成员 Sriram Krishnan 的作品。是他用业余时间开发的。最新的情况是,微软的 Popfly 网站已经“悄悄地”的做了更新,就是采用了 Krishnan 的 Cacheman,更新了缓存机制。该项缓存技术更新带来的性能提升非常显著,根据Popfly团队中的 John Montgomery 的说法:加载一个已有的Mashup应用时,可以带来2到6倍的性能提升。
这些说法也得到了 Krishnan 本人的确认。他提到这是Cacheman 的第一次的实际应用,并自豪的说 Cacheman 不费吹灰之力就拿下了 Popfly 的全部访问量。
简单介绍一下 Cacheman 这个项目。资料主要来源于 Krishnan的博客对Cacheman的介绍。
Cacheman是一个基于Windows平台的快速分布式哈希表。是由纯托管代码实现。中间搁置了有几个月,直到最近才开始重新上马这个项目,极可能就是因为Popfly项目需要的缘故才开始着手的。
Krishnan本人对 memcached 很感兴趣,于是创建了 Cacheman。Cacheman上有很多 memcached 的影子,比如与memcached相似的文本通讯协议。Cacheman的通讯协议公开,任何人可以根据自己偏爱的语言环境写客户端。 Krishnan 在自己家用电脑(2.4GHz Intel Core 2 带2GB内存)上进入测试,达到了每秒16000次左右的请求,并且还是服务器与客户端都是在同一台服务器下完成的。
现这款产品还不太完善,作者自身也提到:在Cacheman做指定key的GET/SET/DELETE操作时,客户端需要弄清需要与哪一台Cacheman服务器通讯,为此要对该key做一个快速FNV哈希然后求余得到应该和几台服务器中的哪台服务器通讯。但该法的缺点在于新增或删除一个服务器节点时,缓存节点需要大规模迁移。修复该问题需要一致性的哈希算法,作者表示还没有时间解决此事。作者提出了采用中心架构的“主缓存服务器”的解决办法,让客户端轮询主缓存服务器来获取应该与那个缓存服务器通讯,但他也觉的这样做增加了复杂性,会带来些新问题。
可以感觉到,由于 Cacheman 这个个人项目已经介入到 Popfly 这个正式产品中,可能很快就会被微软吸纳为正式产品,因此如果有人采用这个产品做自己缓存的解决方案的话,应该不必太担心后续的产品升级及文档支持服务,它的未来前途值的期待。说不定 Krishnan 会从 Popfly 项目脱身出来专职负责这个 Cacheman 项目。
以上内容转自:http://www.cnblogs.com/ltp/archive/2009/06/30/1514311.html