1.缓存是提高程序性能的一种非常重要的方式,其原理就是通过空间换取时间,在内存中存储CPU的运算结果,这样下次相同的运算时,可直接从缓存中提取,提高系统性能。
2.以下情况一般都会用到缓存:数据库查询,系统配置模块,对象池等。
3.缓存运用的一些特点:运算比较復杂或耗时,运算结果是与运算条件一一对应的(如通过账号查询user,账号与DataSet对象是一一对应的),但是其数量是可变的(缓存的项次与Client的查询请求和资料库中user的数量有关)。
4.如果某个类出现以下类似代码:
2
3 public object GetSomeThing(string key){
4 if(_store.ContainsKey(key))
5 return _store[key];
6 else{
7 object o = 计算(key);
8 _store.Add(key,o);
9 return o;
10 }
11 }
那么就要考虑这是否属于缓存的范畴了。
5.缓存设计有关的概念:
a.计算类:如资料库查询,获取配置,创建对象等工具类别,该类或模块不依赖缓存类,只完成运算,缓存类对其是透明的。
b.Client:请求计算类进行计算,并得到结果。Client只依赖计算类,缓存对於该类来说也是透明的,即它不知道运算结果是从缓存中得来,还是直接运算得来的。
c.缓存工具类:该类是一个独立的工具类,完成如存储,获取,清空,多线程读写,过期策略等缓存功能。
在asp.net中可以利用已经实现多线程读写完全,支持丰富的过期策略的HttpCache对象,对其进行简单的包装就可以完成了。
如果是需要缓存大数据量的系统,考虑现在比较流行的key/value存储memcached,实现多主机缓存等更稳定,扩展性更强的专业方案。
d.具体缓存类:
该类实现在Client端请求计算类计算时进行拦截,如果该运算结果已在缓存中,则直接命中缓存完成,如果无运算结果,则调用计算类得到结果,并将结果存入缓存,以备下次再用。
该类的另一个重要部份就是要负责缓存的清空,具体清空方式与实际的计算类有关,如配置获取类的缓存依赖于配置是否修改。数据库查询结果的缓存可能与table有关,因此侦测到对该table的增,删,改时,要清空相应的缓存,另外查询结果可能太多,也可以通过设定低优先级以及固定过期策略来自动清空缓存。
由于具体缓存类依赖于计算类,因此计算类必须是一种通用的工具类,否则会造成大量的具体缓存类,增加復杂性。
e.具体缓存类与计算类的结合方式:
这是一种典型的AOP应用,如果不喜欢aop框架的复杂与笨拙,可以使用Decorator模式来完成计算类的拦截。
6.计算类示例:对象创建类
2 {
3 //Decorator构建
4 public static readonly ObjectFactory Instance
5
6 public T Create<T>(string objectID)
7 {
8 T ret = 创建对象...
9 return ret;
10 }
11 }
7.具体缓存类(Decorator模式实现)
2 {
3 ObjectFactory _orgFactory; //被包装的计算类
4 CacheTool _cache; //缓存工具类
5 public CacheObjectFactory(ObjectFactory orgFactory)
6 {
7 _cache = new HttpCache(this.GetType().FullName);
8 _orgFactory = orgFactory;
9 }
10
11 public T Create<T>(string objectID){
12 T ret = _cache.Get<T>(objectID);
13 if(ret==null){ //根据具体情况还要考虑是null还是没有cache,以及lock策略
14 ret = _orgFactory.Create<T>(objectID);
15 _cache.Set(objectID,T);
16 }
17 return ret;
18 }
19 }
8.客户端调用示例代码
2 bus.CallSomeMethod();
缓存只是一种提高系统性能的工具,将缓存独立出来设计后,可以使缓存模块在整个系统中透明。这样,在开发某一模块时,可以将与主逻辑无关的系统优化的缓存延后。待主逻辑功能实现后,再专门来考虑如何使用缓存提升模块的性能。而专门的缓存模块设计,对于缓存的重用,也是有很大帮助的。