ASP.NET状缓存Cache的应用-提高数据库读取速度

ASP.NET状缓存Cache的应用-提高数据库读取速度
原文:ASP.NET状缓存Cache的应用-提高数据库读取速度

一、 Cache概述
  
    既然缓存中的数据其实是来自数据库的,那么缓存中的数据如何和数据库进行同步呢?一般来说,缓存中应该存放改动不大或者对数据的实时性没有太多要求的数据。这样,我们只需要定期更新缓存就可以了。相反,如果缓存的更新频率过快的话,使用缓存的意义就不是很大了,因此更新缓存的时候需要一次性从数据库中读取大量的数据,过于频繁地更新缓存反而加重了数据库的负担。
那么ASP.NET中的Cache又提供了哪些缓存的过期策略呢?
· 永不过期。和Application一样,缓存永不过期。
· 绝对时间过期。缓存在某一时间过期,比如5分钟后。
· 变化时间过期(平滑过期)。缓存在某一时间内未访问则超时过期,这个和Session有点类似,比如我们可以设定缓存5分钟没有人访问则过期。
· 依赖过期。缓存依赖于数据库中的数据或者文件中的内容。一旦数据库中某些表的数据发生变动或者文件内容发生变动,则缓存自动过期。
缓存过期后我们就要更新缓存了,ASP.NET提供了两种更新策略。
· 被动更新。缓存过期以后手动进行更新。
· 主动更新。缓存过期以后在回调方法中更新。


二、 Cache性能与过期策略


首先,在页面上添加两个按钮,并双击按钮实现Click事件处理方法。


程序代码 程序代码
<asp:Button ID="btn_GetDataFromCache" runat="server" OnClick="btn_GetData_Click"
Text="从缓存中读取数据" />
<asp:Button ID="btn_GetDataFromDb" runat="server" OnClick="btn_GetDataFromDb_Click"
Text="从数据库中读取数据" />


第一个按钮实现从缓存读取数据。

注意:本例需要using以下命名空间。

程序代码 程序代码
using System.Diagnostics; // 用于精确测定时间间隔
using System.Web.Caching; // 用于缓存的策略
using System.IO;   // 用于文件操作

protected void btn_GetData_Click(object sender, EventArgs e)
{
    InsertRecord();
    Stopwatch sw=new Stopwatch();
    sw.Start();
    if (Cache["Data"]==null)
    {
        Response.Write("缓存无效<br/>");
    }
    else
    {
        DataSet ds = Cache["Data"] as DataSet;
        Response.Write(string.Format("查询结果:{0}<br/>", ds.Tables[0].Rows[0][0]));
        Response.Write(string.Format("耗费时间:{0}<br/>", sw.ElapsedTicks));
    }
}
在这里有几点需要说明。
· 一开始的InsertRecord()方法是我们自己创建的,用来向数据库插入一条记录。这样,我们就能看出来数据是否是从缓存中读取的了。
InsertRecord()方法如下:

程序代码 程序代码
private void InsertRecord()
{
    using (SqlConnection conn = new SqlConnection(@"server=(local)\SQLEXPRESS;
    database=Forum;Trusted_Connection=True"))
    {
        conn.Open();
        using (SqlCommand cmd = new SqlCommand("Insert into CacheTest (Test) values
       ('Test')", conn))
        {
            cmd.ExecuteNonQuery();
        }
    }
}

· 如果缓存存在则输出查询结果和查询耗费的时间,如果缓存不存在则输出“缓存无效”。
· Stopwatch类用于精确测定逝去的时间,ElapsedTicks属性返回了间隔的计数器刻度,所谓计数器刻度就是系统的计数器走过了多少次。当然,Stopwatch还有ElapsedMilliseconds能返回间隔的总毫秒数。之所以使用ElapsedTicks,因为它是一个更小的时间单位。
第二个按钮直接从数据库读取数据。

程序代码 程序代码
protected void btn_GetDataFromDb_Click(object sender, EventArgs e)
{
    InsertRecord();
    Stopwatch sw = new Stopwatch();
    sw.Start();
    DataSet ds = GetData();
    Response.Write(string.Format("查询结果:{0}<br/>", ds.Tables[0].Rows[0][0]));
    Response.Write(string.Format("耗费时间:{0}<br/>", sw.ElapsedTicks));
}
在这里,我们把读取数据的操作使用一个GetData()方法进行了封装,方法实现如下:

程序代码 程序代码
private DataSet GetData()
{
    DataSet ds = new DataSet();
    using (SqlConnection conn = new SqlConnection(@"server=(local)\SQLEXPRESS;
    database=Forum;Trusted_Connection=True"))
    {
        SqlDataAdapter da = new SqlDataAdapter("select count(*) from CacheTest", conn);
        da.Fill(ds);            
    }
    return ds;
}

为了能体现出缓存的效率,我们在Forum数据库中又新建立了一个CacheTest数据表,表结构很简单,如图4-1所示。

图4-1  CacheTest表结构
我们在表中插入了10万条以上的记录,使得表的大小达到了100MB左右。
运行程序,单击“从数据库中读取数据”按钮,如图4-2所示,

图4-2 从数据库读取数据需要花费大量的时间

我们可以看到,这个操作耗费了相当多的时间。
因为我们直接从数据库读取count(*),所以每次单击按钮查询结果显示的数字都会+1。现在你单击“从缓存中读取数据”肯定是显示“缓存无效”,因为我们还没有添加任何缓存。
然后,我们在页面上添加三个按钮并双击按钮创建事件处理方法,三个按钮使用不同的过期策略添加缓存。

程序代码 程序代码
<asp:Button ID="btn_InsertNoExpirationCache" runat="server" Text="插入永不过期缓存"
OnClick="btn_InsertNoExpirationCache_Click" />
<asp:Button ID="btn_InsertAbsoluteExpirationCache" runat="server" Text="插入绝对时间
过期缓存" OnClick="btn_InsertAbsoluteExpirationCache_Click" />
<asp:Button ID="btn_InsertSlidingExpirationCache" runat="server" Text="插入变化时间
过期缓存" OnClick="btn_InsertSlidingExpirationCache_Click" />

三个按钮的Click事件处理方法如下:

程序代码 程序代码
protected void btn_InsertNoExpirationCache_Click(object sender, EventArgs e)
{
    DataSet ds = GetData();
    Cache.Insert("Data", ds);
}
protected void btn_InsertAbsoluteExpirationCache_Click(object sender, EventArgs e)
{
    DataSet ds = GetData();
    Cache.Insert("Data", ds,null, DateTime.Now.AddSeconds(10), TimeSpan.Zero);
}
protected void btn_InsertSlidingExpirationCache_Click(object sender, EventArgs e)
{
    DataSet ds = GetData();
    Cache.Insert("Data", ds, null, DateTime.MaxValue, TimeSpan.FromSeconds(10));
}
我们来分析一下这三种过期策略。
· 永不过期。直接赋值缓存的Key和Value即可
· 绝对时间过期。DateTime.Now.AddSeconds(10)表示缓存在10秒后过期,TimeSpan.Zero表示不使用平滑过期策略。
· 变化时间过期(平滑过期)。DateTime.MaxValue表示不使用绝对时间过期策略,TimeSpan.FromSeconds(10)表示缓存连续10秒没有访问就过期。
在这里,我们都使用了Insert()方法来添加缓存。其实,Cache还有一个Add()方法也能向缓存中添加项。不同之处在于Add()方法只能添加缓存中没有的项,如果添加缓存中已有的项将失败(但不会抛出异常),而Insert()方法能覆盖原来的项。
注意:和Application不同,这里不需要使用在插入缓存的时候进行锁操作,Cache会自己处理     并发。
现在,我们就可以打开页面对这三种过期策略进行测试了。
1.单击“从缓存中读取数据”按钮,提示“缓存无效”。
2.单击“从数据库中读取数据”按钮,查询结果显示现在记录总数为100646。
3.单击“插入永不过期缓存”按钮,然后连续单击“从缓存中读取数据”按钮,可以发现,无论过去多久,缓存始终没有过期,而且观察记录查询结果可以发现值始终没有发生变化。不同的是,从缓存中读取数据的效率比从数据库中读取数据提高了几个数量级,如图4-3所示,你可以和图4-2进行比较。

图4-3  从缓存中读取数据所花费的时间
4.单击“插入绝对时间过期缓存”,然后连续单击“从缓存中读取数据”按钮,大约10秒过期后,页面提示“缓存无效”,说明缓存过期了。
5.单击“插入变化时间过期缓存”,然后连续单击“从缓存中读取数据”按钮,缓存始终不过期,如果我们等待10秒后再去单击按钮,页面提示“缓存无效”,说明缓存过期了。
我们再来看一下依赖过期策略。所谓依赖过期就是缓存的依赖项(比如一个文件)的内容改变之后缓存也就失效了。由于篇幅关系,这里只介绍文件依赖。我们在页面上再加两个按钮并双击按钮添加Click事件处理方法。

程序代码 程序代码
<asp:Button ID="btn_ModifyFile" runat="server" Text="修改文件" OnClick="btn_ModifyFile_
Click" />
<asp:Button ID="btn_AddFileDependencyCache" runat="server" Text="插入文件依赖缓存"
OnClick="btn_AddFileDependencyCache_Click" />


在本例中,我们将使缓存依赖一个txt文本文件。因此,首先在项目中添加一个test.txt文本文件。单击“修改文件”按钮实现文件的修改。

程序代码 程序代码
protected void btn_ModifyFile_Click(object sender, EventArgs e)
{
    FileStream fs = new FileStream(Server.MapPath("test.txt"), FileMode.Append,
    FileAccess.Write);
    StreamWriter sw = new StreamWriter(fs);
    sw.WriteLine(DateTime.Now.ToString());
    sw.Close();
    fs.Close();
}


我们通过在文件的最后写入当前的时间来修改文件。插入文件依赖缓存按钮的事件处理方法如下:

程序代码 程序代码
protected void btn_AddFileDependencyCache_Click(object sender, EventArgs e)
{
    CacheDependency cd = new CacheDependency(Server.MapPath("test.txt"));
    DataSet ds = GetData();
    Cache.Insert("Data", ds, cd);
}
添加文件依赖缓存同样简单,通过CacheDependency关联了一个文件依赖。
现在就可以打开页面进行测试了。
1.单击“从缓存中读取数据”按钮,提示“缓存无效”。
2.单击“从数据库中读取数据”按钮,查询结果显示现在记录总数为100710。
3.单击“插入文件依赖缓存”按钮,然后连续单击“从缓存中读取数据”按钮,可以发现,无论过去多久,缓存始终没有过期,而且观察记录查询结果可以发现值始终没有发生变化。
4.单击“修改文件”按钮,然后单击“从缓存中读取数据”按钮,提示“缓存无效”。由于文件已经修改了,依赖这个文件的缓存立刻失效了。


三、  Cache的更新策略
最后,我们来讨论缓存的更新策略。在Web程序中我们通常会使用被动更新。所谓被动更新,就是在调用数据的时候判断缓存是否为空,如果为空则先更新缓存然后再从缓存中读取数据,如果不为空则直接从缓存中读取数据。可以把“从缓存中读取数据”按钮的Click事件处理方法修改成如下,实现被动更新。


程序代码 程序代码
protected void btn_GetData_Click(object sender, EventArgs e)
{
    InsertRecord();
    DataSet ds = new DataSet();
    Stopwatch sw = new Stopwatch();
    sw.Start();
    if (Cache["Data"] == null)
    {
        ds = GetData();
        Cache.Insert("Data", ds, null, DateTime.Now.AddSeconds(10), TimeSpan.Zero);
    }
    else
    {
        ds = Cache["Data"] as DataSet;
    }
    Response.Write(string.Format("查询结果:{0}<br/>", ds.Tables[0].Rows[0][0]));  
    Response.Write(string.Format("耗费时间:{0}<br/>", sw.ElapsedTicks));
}

我们可以看出,如果没有人访问数据缓存是不会更新的,只有缓存被访问的时候发现缓存无效才会去更新。这样很明显的一个缺点就是,如果缓存过期了更新操作将花费很长时间,这个时候的查询也需要花费很多时间。我们可以利用缓存的回调功能让缓存过期后自动续建实现自动更新的目的。

程序代码 程序代码
protected void btn_InsertActiveUpdateCache_Click(object sender, EventArgs e)
{
    DataSet ds = GetData();
    Cache.Insert("Data", ds, null, DateTime.Now.AddSeconds(10), TimeSpan.Zero,
    CacheItemPriority.Default, CacheRemovedCallback);
}

最后一个参数表明缓存被移除以后自动调用CacheRemovedCallback()方法,方法实现如下。

程序代码 程序代码
private void CacheRemovedCallback(String key, object value, CacheItemRemovedReason
removedReason)
{
    DataSet ds = GetData();
    Cache.Insert(key, ds, null, DateTime.Now.AddSeconds(10), TimeSpan.Zero, CacheItemPriority.
    Default, CacheRemovedCallback);
}


在回调方法中,我们再次插入一个支持回调的缓存。这样,缓存被移除以后又能自动更新了。说了这么多创建缓存的方法,读者可能会问怎么手动移除缓存呢?比如我们要移除Key="Data"的缓存只需要:
Cache.Remove("Data");

你可能会马上想到用Cache.RemoveAll()方法移除所有缓存,可是Cache没有提供这样的方法,我们只能通过遍历来实现移除所有缓存。

程序代码 程序代码
IDictionaryEnumerator CacheEnum = HttpRuntime.Cache.GetEnumerator();
while (CacheEnum.MoveNext())
{
    Cache.Remove(CacheEnum.Key.ToString());
}



四、  Cache总结
同样,我们以第一节中的几个问题结束对Cache的讨论。
· 存储的物理位置。服务器内存。
· 存储的类型限制。任意类型。
· 状态使用的范围。当前请求上下文,所有用户共用一份。
· 存储的大小限制。任意大小。
· 生命周期。有多种过期策略控制缓存的销毁。
· 安全与性能。数据总是存储在服务端,安全性比较高,但不易存储过多数据。
· 优缺点与注意事项。检索数据速度快,过期策略丰富。注意别把对实时性要求很高的数据放到Cache中,不断更新Cache会对数据库造成压力。

posted on 2015-12-09 08:59 NET未来之路 阅读(...) 评论(...) 编辑 收藏

转载于:https://www.cnblogs.com/lonelyxmas/p/5031741.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/458583.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

入门级----测试的执行、环境的搭建、每日构建、测试记录和跟踪、回归测试、测试总结和报告...

测试用例的准备&#xff0c;都是为了执行测试准备的。 测试环境的搭建 &#xff08;1&#xff09;测试数据&#xff1a;有些测试需要使用大批量的数据&#xff0c;例如容量测试、压力测试等。根据产品的具体测试要求&#xff0c;可能需要在数据库表插入大量的数据&#xff0c;准…

限制MySQL Binlog的传输速率

最近一台核心库备库完成恢复后打开slave&#xff0c;导致主库传送binlog&#xff0c;瞬间占满网络&#xff0c;触发故障。 为了做一些限制&#xff0c; 给mysql在发送binlog的函数(mysql_binlog_send)里每隔一段时间sleep一次&#xff0c; 增加了两个参数&#xff1a; master_s…

掌握 Ajax,第 2 部分: 使用 JavaScript 和 Ajax 发出异步请求

转http://www.ibm.com/developerworks/cn/xml/wa-ajaxintro2/ 掌握 Ajax&#xff0c;第 2 部分: 使用 JavaScript 和 Ajax 发出异步请求 在 Web 请求中使用 XMLHttpRequest 多数 Web 应用程序都使用请求/响应模型从服务器上获得完整的 HTML 页面。常常是点击一个按钮&#xff0…

Provisioning Services 7.8 入门系列教程之十一 通过版本控制自动更新虚拟磁盘

续Provisioning Services 7.8 入门系列教程之十 通过类自动更新虚拟磁盘从前两的两种更新方式可以看出&#xff0c;它们有一个共同的特点&#xff0c;即需要产生&#xff08;复制&#xff09;完成的虚拟磁盘副本&#xff0c;然后进行相关的升级操作。这两种方法在实际生产中&am…

登录失败时记住访问的地址

登录失败时记住访问的地址 使用spring MVC 访问时,在拦截器中记录访问的地址: Java代码 String path request.getRequestURI();//"/demo_channel_terminal/news/list" System.out.println("您无权访问:" path); //用于登录成功…

Tomcat - Maven plugin: 运行找不到webapp

2019独角兽企业重金招聘Python工程师标准>>> The tomcat7-maven-plugin allows running the current project as a Web application and additional <webapps> can be specified that will be simultaneously loaded into tomcat. My project is not a Web ap…

基于MQTT协议进行应用开发

来自&#xff1a;http://www.cnblogs.com/secondtononewe/p/6073089.html 官方协议有句如下的话来形容MQTT的设计思想&#xff1a; “It is designed for connections with remote locations where a "small code footprint" is required or the network bandwidth i…

SortedDictionaryTKey,TValue正序与反序排序及Dicttionary相关

SortedDictionary<TKey,TValue>能对字典排序 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace SortDictionary {class Program{static void Main(string[] args){TestDictionarySort();…

DOS窗口的编码页从UTF-8调回GBK

2019独角兽企业重金招聘Python工程师标准>>> 之前在DOS窗口操作MySQL数据库的时候&#xff0c;将编码页从GBK设置成了UTF-8&#xff0c;解决了在DOS窗口显示MySQL数据库中的表中的中文字符出现乱码的问题。但是除此之外&#xff0c;DOS窗口显示的其他中文字符都是乱…

UIBezierPath

学习UIBezierPath画图 笔者在写本篇文章之前&#xff0c;也没有系统学习过贝塞尔曲线&#xff0c;只是曾经某一次的需求需要使用到&#xff0c;才临时百度看了一看而且使用最基本的功能。现在总算有时间停下来好好研究研究这个神奇而伟大的贝塞尔先生&#xff01; 笔者在学习时…

系统架构设计理论与原则

一、无共享架构 1、无共享架构 无共享架构是一种分布式计算架构&#xff0c;这种架构中不存在集中存储的状态&#xff0c;系统中每个节点都是独立自治的&#xff0c;整个系统中没有资源竞争&#xff0c;这种架构具有非常强的扩张性&#xff0c;目前在web应用中被广泛使用。 无共…

VS2010 教程:创建一个 WPF 应用程序 (第一节)

来自&#xff1a;https://msdn.microsoft.com/zh-cn/library/ff629048.aspx [原文发表地址] VS2010 Tutorial: Build a WPF App (Step 1) [原文发表时间] Friday, May 22, 2009 8:00 AM 这篇文章里&#xff0c;我将使用VS2010 Beta 1创建一个WPF 应用程序。并且 我将展示这个产…

js 日期星期 带农历

Weekday代码 //得到当前日期如2009年6月19日 星期五 function getDate(){ var today new Date(); var x new Array("星期日", "星期一", "星期二","星期三","星期四", "星期五","星期六"…

iOS 推送

最近在研究ios的推送问题&#xff0c;遇到了一些问题&#xff0c;最终整理了一下。放在这里和大家分享APNS的推送机制首先我们看一下苹果官方给出的对ios推送机制的解释。如下图 Provider就是我们自己程序的后台服务器&#xff0c;APNS是Apple Push Notification Service的缩写…

iOS 加载本地html文件详细操作

webView的加载&#xff0c;如果是纯文本&#xff0c;有内部样式的话&#xff0c;简单的加载请求就可以了。如下: 这种加载简单直接&#xff0c;易操作。 如果需要加载images&#xff0c;css文件 需要把xcode的项目请求路径的位置告诉webView.代码如下&#xff1a; 这样html文件…

[原]详解如何将cocos2dx项目编译到Android平台上的(方式一:Cywin+NDK)

链接地址&#xff1a;http://m.blog.csdn.net/blog/yhc13429826359/29357815 2014-6-8阅读578 评论0 前言&#xff1a;cocos2dx作为一个开源的移动2D游戏框架&#xff0c;其跨平台的特性让它备受开发公司的欢迎。这里我就不做概念性的解释了&#xff0c;通过这篇文章你就会了解…

JSP EL表达式使用

为什么80%的码农都做不了架构师&#xff1f;>>> ##1.EL全名为Expression Language out.print(str) <%str%> ${str}例子&#xff1a; Hi! <%username%> 和 Hi! ${username}是一样的 只要是支持servlet2.4/jsp2.0的Container就都可以在jsp网页中直接使用e…

eclipse 中 Android sdk 无法更新的问题

诶&#xff0c;真是麻烦&#xff0c;想下个东西都下不了。我也好久没折腾过这个了&#xff0c;在家的电脑是早就下载好了的&#xff0c;然后如今又须要下载一份。下不到。网上搜到了资料&#xff0c;记录下来&#xff1a; 第一种方法:sdk manager - tools - option 选择强制 xx…

iOS10 xcode8 分页请求MJRefresh崩溃问题

MJRefresh出现崩溃现象 解决办法&#xff1a;类库增加判断 if (range.location ! NSNotFound) { language [language substringToIndex:range.location]; }

网络编程学习笔记一:Socket编程

from: http://blog.csdn.net/gneveek/article/details/8699198 “一切皆Socket&#xff01;” 话虽些许夸张&#xff0c;但是事实也是&#xff0c;现在的网络编程几乎都是用的socket。 ——有感于实际编程和开源项目研究。 我们深谙信息交流的价值&#xff0c;那网络中进程之间…