学习之路十九:模仿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]
  }

 

五丶总结

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

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

 

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

posted @ 2012-11-24 16:07  TimYang  阅读(1875)  评论(15编辑  收藏  举报