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


声明:本文为做者原创文章,转载请注明出处 http://www.javashuo.com/article/p-abxcnagj-de.html 

问题背景:

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

  之前也有作过这个功能,只不过此次作这个功能的时候冒出了一个想法:web

  我请求的这段数据它是一段相对比较固定的数据,也就是说它不怎么改变,也许几个月才会改变一次。因为这种数据的变化周期很长,因此之前作这种功能的时候,会使用缓存进行优化,能够直接从缓存中读取数据,避免每一次接收了ajax请求后都要向数据库要数据,减小服务器与数据库之间的交互,减轻数据库服务器的压力。可是问题来了,数据的变化周期再长终究是要变化的,当数据库中的数据变化的时候你就要对旧有的缓存内容进行移除(remove)操做,好的,之前我是这样作的:ajax

        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方法,其中有一个重要的方法sql

public void Insert(数据库

  string key,                                             --> 缓存项的键json

  object value,                --> 缓存项的值
数组

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

  DateTime absoluteExpiration,               --> 绝对过时时间点缓存

  TimeSpan slidingExpiration                   --> 滑动过时时间间隔服务器

);

 

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

第一次请求

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

 

(执行结果):

 

第一次请求的话确定缓存中是没有数据的,因此要向数据库要数据;

 

 

第二次请求

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

 

 

 

 

 

(执行结果):

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

 

 

过30s后再请求

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

 

(执行结果):

 

缓存中数据已通过期被移除,须要从新向数据库请求;

 

 

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

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

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

 

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

 

数据库依赖缓存,对,是它,就是它。

 

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

  • 缓存项目依赖
  • 文件或文件夹依赖
  • 数据库依赖

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

  之前不知道有 数据库依赖 的时候,有想过使用文件或文件夹依赖来实现相似于数据库依赖缓存的功能,大概的思路就是:用某个文件做为媒介,在进行了对数据库表的数据改动后,同时改动一下该文件来触发缓存的失效。还好我在看了大神们写的博文之后,立刻中止了我“愚蠢”的想法,那样作实际上是多此一举,并且并不能很好的实现,有现成的数据库依赖不用干吗。

 

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

 一、配置:

  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, "(表名)"); //第二个参数能够是单个表名或表名的数组

 

 

 

 二、代码部分

 

        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,在这里:

 

内容是:

 

因此最后对原理的总结,我就引用一篇大神博客(https://www.lanhusoft.com/Article/290.html)里的总结:

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

 

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

 

参考博文地址

  https://www.cnblogs.com/SkySoot/archive/2012/08/15/2640559.html

  https://www.lanhusoft.com/Article/290.html

  这是个人第一篇博文,之前想过为何要写博文这个问题?如今网上随便一搜都有本身想要的内容,并且有些大神写的很不错。后来慢慢的发现每一次遇到问题都要上网搜博客看,并且不少时候遇到的是同一个问题,可能之前就已经思考过了,如今又要浪费时间去从头找着看已经看过的东西,那我为何不本身写好了整理好了存着呢。固然,方便本身的同时,但愿也能方便到正在阅读本文的你。谢谢!

相关文章
相关标签/搜索