学习之路十九:模仿ASP.NET的缓存依赖自定义缓存机制
项目的性能一直处于中下等的水平,也一直在摸索着,自从前几周优化了WCF那一块之后,速度明显提高了,可是程序还不是理想中的要求,所以还要继续努力!
一丶情境
前一段时间发现多个客户端在第一次连接Server的时候会频繁获取同样的数据,也就说每个客户端获取数据都要去数据库查找一次,同一时刻多次的去数据库获取数据也是一件很费时间的事!
想来想去,决定把那些基本不变的数据缓存在Server,最后客户端获取数据的时候就不需要直接查找数据库了!
上网查了一下关于设计缓存机制的资料,大部分都是ASP.NET Cache的文章!
虽然Winform也可以使用ASP.NET Cache,不过在我逐步研究的时候,发现它并不适合我们的项目,主要有下面几个原因:
1. 需要把连接字符串配置到App.config中,而我们项目中的连接字符串是单独放在一个指定的文件中的(这是阻碍我使用ASP.NET Cache的最大原因)
2. 有的时候需要使用命令来开始SQL Server缓存依赖通知(或者通过代码来开启),以及配置文件也需要一些修改
推荐文章:① 细说 ASP.NET Cache 及其高级用法
二丶学习
虽然不能把它应用到我们项目中,不过我还是继续研究了一下它,并且在学习中我找到了一些自定义缓存的灵感!
当数据库数据发生更新时,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]
}
五丶总结
经过了两三周的辛苦努力终于结束了,其实这个过程也痛苦的(主要是不怎么会设计代码,想的头都大了),不过结果还是挺好的!
如果觉得文章里哪里说的不好的,还请指出啊,我知道代码还需要修改,大牛们可以多提提建议哦,一定会虚心接受的!
已同步至:程序猿个人文章目录索引