• 学习之路十九:模仿ASP.NET的缓存依赖自定义缓存机制


    项目的性能一直处于中下等的水平,也一直在摸索着,自从前几周优化了WCF那一块之后,速度明显提高了,可是程序还不是理想中的要求,所以还要继续努力!

    一丶情境

    前一段时间发现多个客户端在第一次连接Server的时候会频繁获取同样的数据,也就说每个客户端获取数据都要去数据库查找一次,同一时刻多次的去数据库获取数据也是一件很费时间的事!

    想来想去,决定把那些基本不变的数据缓存在Server,最后客户端获取数据的时候就不需要直接查找数据库了!

    上网查了一下关于设计缓存机制的资料,大部分都是ASP.NET Cache的文章!

    虽然Winform也可以使用ASP.NET Cache,不过在我逐步研究的时候,发现它并不适合我们的项目,主要有下面几个原因:

    1. 需要把连接字符串配置到App.config中,而我们项目中的连接字符串是单独放在一个指定的文件中的(这是阻碍我使用ASP.NET Cache的最大原因)

    2. 有的时候需要使用命令来开始SQL Server缓存依赖通知(或者通过代码来开启),以及配置文件也需要一些修改

    推荐文章:① 细说 ASP.NET Cache 及其高级用法

         ② PetShop之ASP.NET缓存

         ③ 并发环境下的缓存容器性能优化(上):不可变的哈希表

         ④ 系统缓存全解析6:数据库缓存依赖

    二丶学习

    虽然不能把它应用到我们项目中,不过我还是继续研究了一下它,并且在学习中我找到了一些自定义缓存的灵感!

    当数据库数据发生更新时,ASP.NET Cache为什么会自动删除缓存数据呢,最重要的有两点:

    1. 当开启缓存依赖的时候,会在你需要缓存的表中添加一个触发器,然后会有一张表专门记录缓存的表(由触发器添加)最大更新时间

    2. 会轮询数据库,也就是开启一个Timer一直去监测数据库

    三丶思考

    所以鉴于以上的想法,我决定自定义缓存,想法有三个方面:

    1. 也是开启一个Timer一直去监测你要缓存的数据表(每创建一个缓存依赖就会启动一个Timer)

    2. 定义一个HasChange属性(学习ASP.NET Cache的),记录数据库表数据是否发生改变了

    3. 我们数据库中每一张表都有一个UpDT的字段,专门记录最近更新的时间(我可以通过比较前后两次时间来判断数据是否发生了变化)

    四丶实现

      1. 定义一个缓存基类(BaseCache),如下:

     1     public class BaseCache
     2     {
     3         private DateTime? _tableMaxUpdateTime = DateTime.Now; // 第一次记录表中数据的最大更新时间
     4         private string _getMaxUpdateTimeSQL = string.Empty;
     5         private Timer _timer = new Timer(3000); // 开启时钟一直监测数据库是否变化
     6         private bool _hasChange = false;
     7 
     8         protected BaseCache(string getMaxUpdateTimeSQL)
     9         {
    10             _getMaxUpdateTimeSQL = getMaxUpdateTimeSQL;
    11             _tableMaxUpdateTime = GetMaxUpdateTimeByTable(getMaxUpdateTimeSQL);  //第一次获取最新时间
    12             _timer.Elapsed += _timer_Elapsed;
    13             _timer.Start();
    14         }
    15 
    16         protected bool HasChange  //数据是否发生变化的标记
    17         {
    18             get { return _hasChange; }
    19             set { _hasChange = value; }
    20         }
    21 
    22         private void _timer_Elapsed(object sender, ElapsedEventArgs e)  //轮询数据库
    23         {
    24             try
    25             {
    26                 DateTime? latestMaxUpdateTime = GetMaxUpdateTimeByTable(_getMaxUpdateTimeSQL); //获取最新的更新时间
    27                 if (Convert.ToDateTime(_tableMaxUpdateTime).CompareTo(latestMaxUpdateTime) != 0)  //比较前后两次的时间是否相同
    28                 {
    29                     _tableMaxUpdateTime = latestMaxUpdateTime;
    30                     HasChange = true;
    31                 }
    32             }
    33             catch (Exception exception)
    34             {
    35                 LoggingManager.WriteLog(LogLevel.Exception, exception.Message + Environment.NewLine + exception.StackTrace );
    36             }
    37         }
    38 
    39         private DateTime? GetMaxUpdateTimeByTable(string getMaxUpdateTimeSQL)
    40         {
    41             return new DAL.Cache().GetMaxUpdateTime(getMaxUpdateTimeSQL);
    42         }
    43 
    44         public virtual void LoadCache() { }
    45     }

       2. 定义具体的缓存依赖项 ProductClassCacheDependency,继承BaseCache

     1     public class ProductClassCacheDependency : BaseCache
     2     {
     3         private static Dictionary<int, ProClassInfo> _productClassCache = new Dictionary<int, ProClassInfo>(150);  //通过定义静态变量来作为缓存容器
     4         private static object _obj = new object();
     5 
     6         public ProductClassCacheDependency(string getMaxUpdateTimeSQL)
     7             : base(getMaxUpdateTimeSQL)
     8         {
     9             LoadCache();
    10         }
    11 
    12         public override void LoadCache()
    13         {
    14             Dictionary<int, ProClassInfo> productCalssInfo = new ProClass().GetAllProductClassInfos();
    15             if (productCalssInfo != null)
    16             {
    17                 _productClassCache.Clear();
    18                 _productClassCache = productCalssInfo;
    19             }
    20         }
    21 
    22         public Dictionary<int, ProClassInfo> ProductClassCache
    23         {
    24             get { return UpdateCache(); }
    25         }
    26 
    27         public Dictionary<int, ProClassInfo> UpdateCache()
    28         {
    29             if (HasChange) //采用双判断和锁来实现同步机制
    30             {
    31                 lock (_obj) 
    32                 {
    33                     if (HasChange)
    34                     {
    35                         LoadCache();
    36                         HasChange = false;
    37                     }
    38                 }
    39             }
    40             return _productClassCache;
    41         }
    42     }

       3. 定义缓存代理CacheProxy,调用缓存的入口点

     1     public class CacheProxy
     2     {
     3         private static CacheProxy _cacheProxy = null;
     4         private static object _obj = new object(); 6         private static ProductClassCacheDependency _productClassCache = null; 8 
     9         private CacheProxy() { }
    10 
    11         static CacheProxy() { }
    12 
    13         public static CacheProxy Cache
    14         {
    15             get
    16             {
    17                 if (_cacheProxy == null)
    18                 {
    19                     lock (_obj)
    20                     {
    21                         return _cacheProxy ?? (_cacheProxy = new CacheProxy());
    22                     }
    23                 }
    24                 return _cacheProxy;
    25             }
    26         }
    27 
    33         public ProductClassCacheDependency ProductClassCache
    34         {
    35             get { return _productClassCache ?? (_productClassCache = new ProductClassCacheDependency("SELECT MAX(UpDT) FROM proDTProClass")); }  
    42        //这边可能处理的不好,没有ASP.NET Cache直接传一个表名好,需要改进
    36 } 43 public void LoadAllCache() //程序启动的时候就加载所有的缓存 44 { 46 _productClassCache = ProductClassCache; 47 } 48 }

       4. 调用方式

    1  if(CacheProxy.Cache.ProductClassCache.ProductClassCache.ContainsKey(1) && CacheProxy.Cache.ProductClassCache.ProductClassCache != null)
      {
        CacheProxy.Cache.ProductClassCache.ProductClassCache[1]
      }

    五丶总结

      经过了两三周的辛苦努力终于结束了,其实这个过程也痛苦的(主要是不怎么会设计代码,想的头都大了),不过结果还是挺好的!

      如果觉得文章里哪里说的不好的,还请指出啊,我知道代码还需要修改,大牛们可以多提提建议哦,一定会虚心接受的!

    已同步至:程序猿个人文章目录索引

  • 相关阅读:
    持续集成-禅道
    nohup.out 日志切分
    Flannel 介绍及使用场景
    【Unity游戏开发】初探Unity动画优化
    fastHttp服务端处理请求的过程
    PHPExcel导出文件代码实现
    PHPExcel 1.8
    CKfinder 安装与使用
    Ckeditor的配置
    Ckeditor的使用
  • 原文地址:https://www.cnblogs.com/yangcaogui/p/2781445.html
Copyright © 2020-2023  润新知