ASP.NET MVC 数据库依赖缓存

 

问题背景

最近做一个非常简单的功能,就是使用ajax请求的方式从服务端请求一段下拉表的数据。

以前也有做过这个功能,只不过这次做这个功能的时候冒出了一个想法:

我请求的这段数据它是一段相对比较固定的数据,也就是说它不怎么改变,也许几个月才会改变一次。

由于这种数据的变化周期很长,所以以前做这种功能的时候,会使用缓存进行优化,可以直接从缓存中读取数据,避免每一次接收了ajax请求后都要向数据库要数据,减少服务器与数据库之间的交互,减轻数据库服务器的压力。

但是问题来了,数据的变化周期再长终究是要变化的,当数据库中的数据变化的时候你就要对旧有的缓存内容进行移除(remove)操作,好的,以前我是这样做的:

public ActionResult GetAllFJs()

{

//尝试从缓存中取出数据

var FJIdNames = HttpContext.Cache["FJIdNames"];

if (null == FJIdNames)   //缓存中没有数据

{

//从数据库中查询出数据

FJIdNames = FJService.GetAll().Select(fj => new { Id = fj.Id, Name = fj.Name }).ToArray();

//将数据缓存起来下一次用(设置一个缓存有效时间,使用绝对过期,到达指定过期时间,缓存失效)

HttpContext.Cache.Insert(

"FJIdNames",       //缓存项的键(string)

FJIdNames,         //缓存项的值(object)

null,

DateTime.UtcNow.AddMinutes(30),                 //绝对到期时间(DateTime)

System.Web.Caching.Cache.NoSlidingExpiration    //滑动到期间隔时间(TimeSpan)

);

}

//将得到的数据转化成json格式字符串

string jsonResult = CommonHelper.ConvertToJsonStr(FJIdNames);

//返回给浏览器的结果字符串

return Content(jsonResult);

}

说明:这是一个 asp.net MVC 中处理一个请求的action方法,其中有一个重要的方法

public void Insert(

  string key,                                             --> 缓存项的键

  object value,                --> 缓存项的值

  CacheDependency dependencies,           --> 缓存依赖项(这里不用,后面会用,是重头戏)

  DateTime absoluteExpiration,               --> 绝对过期时间点

  TimeSpan slidingExpiration                   --> 滑动过期时间间隔

);

在这里是利用了缓存的过期时间来对缓存数据进行更新操作,每当缓存数据经过了30分钟后就要重新从数据库中拿一次数据来更新缓存中的内容,来看一下执行的结果   (为了便于展示结果,将绝对过期时间调为30s):

第一次请求:

(数据库表中此刻的数据):

(执行结果):

第一次请求的话肯定缓存中是没有数据的,所以要向数据库要数据;

第二次请求:

(请求之前改变一下数据库中相应表的数据)

(执行结果):

第二次请求,由于没有到缓存项的绝对过期时间,缓存中还有数据,不用向数据库要数据,从缓存中取出来就行;

过30s后再请求:

(数据库表中此刻的数据):

(执行结果):

缓存中数据已经过期被移除,需要重新向数据库请求;

从以上三次请求可以看到,最后的确是实现了缓存数据的更新。

但同样可以看到这样做有一个坏处:在还没到达过期时间的这段时间里,请求的数据依然是原来的缓存中数据,和数据库中的数据并不一致。

其中设置的绝对过期时间点要根据实际的数据刷新的可容忍度来进行设定,而恰好在我的这个应用场景中的可容忍度最不能把握,它要求的是 当数据库中的数据改变以后,缓存中对应的数据在下一次请求结束后一定要马上跟着改变,当然你也可以把过期时间尽可能的调小,调到一秒。当然,这样的话还是要频繁的向数据库进行请求,那不是背离了我们原本使用缓存优化的目的了吗?

所以现在的问题是:有没有一种方法能让数据库和服务器程序建立一种联系,这种联系好比是一种“心灵感应”,当数据库表中的数据发生变化的时候,马上就能让服务器中的对应的缓存项“感应”到这个变化,从而让原来的缓存项失效呢?答案当然是,有的。

数据库依赖缓存

ASP.NET 有 3 种类型的依赖:

  • 缓存项目依赖

  • 文件或文件夹依赖

  • 数据库依赖

本文要讲的是数据库依赖:数据库缓存依赖是一项当数据库中的相关数据被修改时自动使缓存的数据对象失效的技术。

以前不知道有 数据库依赖 的时候,有想过使用文件或文件夹依赖来实现类似于数据库依赖缓存的功能,大概的思路就是:用某个文件作为媒介,在进行了对数据库表的数据改动后,同时改动一下该文件来触发缓存的失效。

还好我在看了大神们写的博文以后,马上停止了我“愚蠢”的想法,那样做其实是画蛇添足,而且并不能很好的实现,有现成的数据库依赖不用干嘛。

我们来看一下如何使用它:

1、配置:

1)在当前网站mvc项目下的Web.config文件中加入

<!--(数据库连接字符串)<configuration> 结点下配置-->

<connectionStrings>

<add name="connStr" connectionString="server=127.0.0.1; user id=sa; password=root; database=LZXF" providerName="System.Data.SqlClient" />

</connectionStrings>

<!--(缓存数据库依赖配置)<system.web> 结点下配置-->

<caching>

<sqlCacheDependency enabled="true">

<databases>

<add name="LZXF" pollTime="5000" connectionStringName="connStr" />

</databases>

</sqlCacheDependency>

</caching>

2)在 Application_Start() 中加入

//配缓存数据库依赖

string connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["connStr"].ToString();

//启动数据库的数据缓存依赖功能

SqlCacheDependencyAdmin.EnableNotifications(connectionString);

//启用数据表缓存

SqlCacheDependencyAdmin.EnableTableForNotifications(connectionString, "(表名)"); //第二个参数可以是单个表名或表名的数组

2、代码部分

public ActionResult GetAllFJs()

{

//尝试从缓存中取出数据

var FJIdNames = HttpContext.Cache["FJIdNames"];

if (null == FJIdNames)   //缓存中没有数据

{

//从数据库中查询出数据

FJIdNames = FJService.GetAll().Select(fj => new { Id = fj.Id, Name = fj.Name }).ToArray();

//将数据缓存起来下一次用(使用数据库依赖的缓存,当数据库中对应的表的数据发生改变时,缓存失效)

HttpContext.Cache.Insert("FJIdNames", FJIdNames, new SqlCacheDependency("LZXF", "T_FJs"));

}

//将得到的数据转化成json格式字符串

string jsonResult = CommonHelper.ConvertToJsonStr(FJIdNames);

//返回给浏览器的结果字符串

return Content(jsonResult);

}

其中的 SqlCacheDependency(数据库缓存依赖类) 是最重要的一个类,就是它建立起了数据库和服务器程序之间 “沟通的桥梁” ,

使用它的一个构造方法:

public SqlCacheDependency(string databaseEntryName, string tableName) 来创建一个数据库缓存依赖类对象,传给创建缓存项的方法Insert, 这样来建立该缓存项的数据库依赖,每当该指定表中发生数据的变动时都会销毁该缓存项。

看一下执行结果:

没改变数据库表数据之前:

(执行结果):

改变数据库表数据之后:

(执行结果):

改变了数据库表的数据以后再去请求数据,请求到最新的数据,说明旧的缓存被移除了

既然都会用了,那接下来要看的就是原理,(原理,原理最重要)

用完了以后就会很疑惑,它是怎么实现的呢?

思考:首先是改动了数据库表中的数据,这一步操作以后必定要引发某种操作。在数据库中改变表中数据会触发操作?讲的不就是触发器吗。

来看一下数据库中多了些什么:

打开sqlServerManagement查看数据库发现

多了一个 AspNet_SqlCacheTablesForChangeNotification 表,表中内容:

要找的触发器在这里:

看一下触发器里面的内容:

说的就是:当 T_FJs 表中发生了 INSERT, UPDATE, DELETE 这些操作,就去执行 dbo.AspNet_SqlCacheUpdateChangeIdStoredProcedure 这个存储过程,这个存储过程是我们开启sql server的缓存依赖自动生成的

找一下存储过程 dbo.AspNet_SqlCacheUpdateChangeIdStoredProcedure,在这里:

内容是:

总结:

当sqlserver启用缓存依赖之后,就会在对应的数据库添加相应的表、触发器和一些存储过程。它是利用触发器来监测表的数据的变化,如果有增、删、改就插入数据到通知表,然后通知订阅这个通知的网站此缓存项失效。

最后要说的是使用缓存依赖也有限制:必须用ASP.Net和SQL Server开发应用,也就是SqlCacheDependency是基于微软的那套体制。

 

ASP.NET MVC 数据库依赖缓存的更多相关文章

  1. ASP.NET MVC 数据库依赖缓存的实现

    当数据库中的信息发生变化的时候,应用程序能够获取变化的通知是缓存依赖得以实现的基础.应用程序可以通过轮询获取数据变化的信息,使用轮询的话也不可能重新查一次后再和以前的数据做比较,如果这样的话如果我一个 ...

  2. 初遇 Asp.net MVC 数据库依赖缓存那些事儿

    问题背景: 最近做一个非常简单的功能,就是使用ajax请求的方式从服务端请求一段下拉表的数据. 以前也有做过这个功能,只不过这次做这个功能的时候冒出了一个想法: 我请求的这段数据它是一段相对比较固定的 ...

  3. ASP.NET MVC IOC依赖注入之Autofac系列(二)- WebForm当中应用

    上一章主要介绍了Autofac在MVC当中的具体应用,本章将继续简单的介绍下Autofac在普通的WebForm当中的使用. PS:目前本人还不知道WebForm页面的构造函数要如何注入,以下在Web ...

  4. 如何在C#Asp.Net MVC使用Redis缓存

    为什么要在Asp.Net MVC项目中使用Redis缓存呢?系统是按照高负载高并发来设计的,这就涉及服务器集群带来的问题,Session存储验证码或登录信息,在系统登录的时候,可能展示登录界面和存储验 ...

  5. YII2数据库依赖缓存

    首先配置一下缓存,自己选择是用文件缓存还是数据库缓存等. 'cache' => [ 'class' => 'yii\caching\FileCache', ], 然后就可以通过 Yii:: ...

  6. 使用Donut Caching和Donut Hole Caching在ASP.NET MVC应用中缓存页面

    Donut Caching是缓存除了部分内容以外的整个页面的最好的方式,在它出现之前,我们使用"输出缓存"来缓存整个页面. 何时使用Donut Caching 假设你有一个应用程序 ...

  7. 007.ASP.NET MVC控制器依赖注入

    原文链接:http://www.codeproject.com/Articles/560798/ASP-NET-MVC-Controller-Dependency-Injection-for-Be 前 ...

  8. 实现asp.net mvc页面二级缓存,提高访问性能

    实现的mvc二级缓存的类 //Asp.Net MVC视图页面二级缓存 public class TwoLevelViewCache : IViewLocationCache { private rea ...

  9. Asp.net Mvc 数据库上下文初始化器

    在Asp.net Mvc 和Entity FrameWork程序中,如果数据库不存在,EF默认的行为是新建一个数据库.如果模型类与已有的数据库不匹配的时候,会抛出一个异常. 通过指定数据库上下文对象初 ...

随机推荐

  1. 十五套专为开发人员打造的PHP资源库

    转载自:http://developer.51cto.com/art/201508/488143.htm 1)Mink Mink是一套PHP 5.3库,用于在测试当中模拟Web应用程序与浏览器之间的交 ...

  2. 通过 thread dump 分析找到高CPU耗用与内存溢出的Java代码

    http://heylinux.com/archives/1085.html通过 thread dump 分析找到高CPU耗用与内存溢出的Java代码 首先,要感谢我的好朋友 钊花 的经验分享. 相信 ...

  3. 前端资源管理工具sourcetree

    https://www.sourcetreeapp.com/

  4. 关于new与=号创建对象的区别

    (1)先定义一个名为str的对String类的对象引用变量:String str: (2)[在[栈]中查找有没有存放值为"abc"的地址,如果没有,则开辟一个存放字面值为" ...

  5. Android新手入门2016(10)--GridView

    本文来自肥宝传说之路.引用必须注明出处! GridView跟ListView一样是多控件布局.实现九宫图是最方便的. 还是先看看图,没图说个鸡鸡是不是 如上图.是一种应用方式.在每一个格子里面.放入应 ...

  6. win10 清理winsxs文件夹

    dism /online /cleanup-image /startcomponentcleanup /resetbase

  7. C 语言 mmap

    /* *@author cody *@date 2014-08-12 *@description */ /* #include <sys/mman.h> void *mmap(void * ...

  8. 查询MySql数据库架构信息:数据库,表,表字段

    /*1.查询所有数据库*/ show databases;  /*2.查询所有数据表*/ select * from information_schema.tables where table_sch ...

  9. jmeter测试http请求使用csv参数

    创建参数化文件 线程组添加CSV Data Set Config配置元件 为请求添加参数 重新运行查看结果 使用随机参数(JMeter有好多函数可以使用) 查看结果发现使用随机生成的数字 3

  10. AlarmManager研究

    1.概述 在Android系统中,闹钟和唤醒功能都是由Alarm Manager Service控制并管理的.我们所熟悉的RTC闹钟以及定时器都和它有莫大的关系.为了便于称呼,我常常也把这个servi ...