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

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

  1. 提高数据库查询速度的几个思路

    提高数据库查询速度的几个思路 : 1.缓存,在持久层或持久层之上做缓存; 2.数据库表的大字段剥离,保证单条记录的数据量很小;  3.恰当地使用索引; 4.必要时建立多级索引;  5.分析Oracle ...

  2. 提高数据库查询速度的方法

    声明:文章是转载的,本文作为个人的备份使用. 处理百万级以上的数据提高查询速度的方法: 1.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描. 2 ...

  3. asp.net中缓存Cache类的使用案例(附源码)

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.C ...

  4. asp.net mvc 缓存CaChe使用

    缓存存储帮助类 using System; using System.Collections.Generic; using System.Linq; using System.Web; using S ...

  5. 提高mysql查询速度_如何提高数据库查询速度

    1.用程序中, 保证在实现功能的基础上,尽量减少对数据库的访问次数: 通过搜索参数,尽量减少对表的访问行数,最小化结果集,从而减轻网络负担: 能够分开的操作尽量分开处理,提高每次的响应速度: 在数据窗 ...

  6. python提高文件读取速度_Python提高了HDF5文件的读取速度

    我正在构建一个存储财务数据的数据库(特别是选项链) . 我每天检索每个交易日的数据,即5d /周或250d /年 . 我这样做了300个代码,数据库正在以150Mb /天的速度增长,到目前为止达到了9 ...

  7. python提升读取速度_如何提高PySerial读取速度

    我目前正在建造一台使用Arduino Mega2560作为主控制器的机器.Arduino连接到over-serial,获取一个命令,执行它,每1毫秒吐出一堆测量数据.我有一个运行Python的Rasp ...

  8. labview提高mysql读取速度_LABVIEW深入探索之MDB数据库写入速度问题。

    近日在LABVIEW的群中,看到几次讨论利用队列把采集的数据写入到数据库的问题,讨论非常热烈,但是他们忽略了一个重要的问题,就是数据库的最大写入速度的问题. 一旦你每秒采样数超过一定限度时,无论你采用 ...

  9. ASP.NE的缓存技术提高Web站点的性能

    一:我们为什么要使用缓存? 先来理解一下asp.net缓存技术的基本原理:把访问频繁的数据以及需要花大量的时间来加载的数据缓存在内存中,那么用户在下次请求同样的数据时,直接将内存中的数据返回给用户,从 ...

最新文章

  1. 分手后我还能和你做朋友么(转)
  2. java上传excel文件代码,求java把上传文件的excel表中数据存入数据库中.实现录入的代码?...
  3. oracle视图不能创建,ORACLEsoctt不能创建视图
  4. Vuejs --01 起步
  5. 【iOS】Tab Bar 的 Item 按钮的像素大小要求
  6. web-----简单小游戏项目
  7. opencv旋转摄像头
  8. 按下()快捷键 可以迅速锁定计算机,电脑锁定的快捷键
  9. Linux服务器安装mysql
  10. 在VB.NET中生成随机数
  11. 大数据分析数据来源的有哪些类型
  12. pwa 让你的网页可以像本地程序一样安装到电脑上,Youtube网站使用的也是当前方法 (chrome版本)
  13. CNN的Python实现——第四章:卷积神经网络的结构
  14. C语言一维数组的定义与常见用法
  15. IDcard 验证代码----新身份证合法性验证
  16. 利用python的docx模块处理word和WPS的docx格式文件
  17. Eclipse在线安装主题(color theme)以及安装color theme第三方主题(图文)
  18. 软件项目外包平台 公司刚刚创业,人手不够怎么办 外包吧
  19. 红米note4X_高通版(2016101)_官方线刷包_救砖包_解账户锁
  20. HTML5和CSS3 介绍开发实战需要的(二)

热门文章

  1. 【POJ - 1995】Raising Modulo Numbers(裸的快速幂)
  2. *【HDU - 4006】 The kth great number(优先队列 or 线段树)
  3. 华为鸿蒙beta版发布会,华为HarmonyOS手机体验鸿蒙版微博:仅9.1MB 附华为鸿蒙发布会直播入口...
  4. c语言中只能逐个引用6,C语言前面六个练习.doc
  5. java request 封装对象_java通过request自动封装复杂对象
  6. 日期时间格式之间的相互转换
  7. 在Spring + Hibernate中使用二级缓存配置步骤
  8. 《Python Cookbook 3rd》笔记(5.21):序列化 Python 对象
  9. sqliteorm的sync_schema介绍
  10. 爸爸的素质决定孩子飞多高,爸爸们请反复看!!!