好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

浅谈缓存管理

浅谈缓存管理

缓存这个东西可大可小,小到一个静态的字段,大到将整个数据库Cache起来。项目开发过程中缓存的应用到处可见,在这篇博文中笔者就来谈谈自己的项目中关于缓存实现。

      最常见的缓存功能,如C#语言中的Dictionary对象,应该至少包含以下几个功能:

Init():缓存的初始化;
如:Dictionary<int, object> dic = new Dictioinary<int, object>(); Add():增加缓存;
如:dic.Add(1, new object()); Set():设置缓存 ;
这里的Set()和Add()是有一点区别的,Add()的时候发现已存在的缓存,则直接返回;而调用Set()的时候会检查,如果不存在执行Add();如果存在,则替换(Remove first,then add)。如:dic[1] = new object(); Reload();重新加载缓存,在某些情况想功能相当于Init;
如:dic=new Dictionary<int, object>(); Remove():移除缓存;
如:dic.Remove(1),(注:执行Remove方法的时候,类似于Set,当key不存在的时候,不作任何处理); Clear():清空缓存。
如:dic.clear();

      如果你用过Dictionary对象的话,相信上面提到的方法对你都不会感到陌生,但是,仅仅一个Dictionary就能够满足我们的需要吗?这里笔者来说说Dictionary的缺点:

一个Dictionary对象的实例是不够用的;
并非所有的缓存的key都是int类型,所以你会在需要的地方不断的定义Dictionary对象,管理起来到底有多难,你懂的。 Dictionary的索引问题;
大家都知道,在进行Dictionary[key]操作的时候,首先要判断是否存在。如果存在,则返回【或许你可以通过Dictionary.TryGetValue(...)方法来避开这一步检查】;如果不存在,则要通过一些方式来获取该对象,然后追加到缓存中去; Dictionary的遍历问题;
或许你会讲,Dictionary是支持foreach遍历的,好吧,这个我也承认;但是,你有没有在有的时候,需要采用for的方式来遍历Dictioanry对象实例呢?那个时候我想你肯定也纠结了一番。 Dictionary的查找问题;
查找?不是可以通过key来索引吗,还需要什么查找?好吧,这个我也承认;但是有些时候,我不仅仅只是需要通过键名key来查找啊,可能还需要通过dic.Find<TValue>(Predicate match), dic.FindAll<TValue>(Predicate match)等方式来查找,这个Dictionary有吗?答案是:没有。 Dictionary的线程安全问题;
这点不多说,因为Dictionary不是线程安全的。 Dictionary的与实际数据的同步问题;
一般来说,既然用到缓存,那么这些数据基本上是读操作远远大于写操作的,所以不可能为了那么一点点的写操作,来额外加多一个定时器进行数据同步,如果是这样的话,如果有100类数据需要进行缓存,那不是伴随着100个定时。,难以想想,这是多么大的一个累赘啊。

      请别上火,因为写了这么多还在说理论;在写我自己的缓存实现的时候,我首先要谈谈我这个缓存管理的应用场景,因为任何一项技术都是因为特定需求而产生的,脱离了实际需求的技术都是空谈,说白了就是无用功。

      在我的项目中,有很多的基础对象,如果数据字典,枚举,模块,权限等数据库对象,这些信息是在项目开发完成后基本上是不会再发生太大的改变的,而且这些数据的访问平率是非常高的,如果每次访问都需要一次数据库连接的话,那对数据库的压力可想而知了。下面是ConcurrentDictionary.cs的代码:

  1   public   class  ConcurrentDictionary<TKey, TData>
  2  {
  3       #region  私有字段
  4  
  5       ///   <summary>
  6        ///  单一源数据(GetOneDataByOneKey)获取器
  7        ///   </summary>
  8       private  Func<TKey, TData> _SourceDataGetter;
  9       ///   <summary>
 10        ///  所有源数据获取器
 11        ///   </summary>
 12       private  Func<List<TData>> _SourceAllDataGetter;
 13       ///   <summary>
 14        ///  缓存存放字典对象
 15        ///   </summary>
 16       private  Dictionary<TKey, TData> _Dic;
 17       ///   <summary>
 18        ///  缓存存放列表对象
 19        ///   </summary>
 20       private  List<TData> _List;
 21       ///   <summary>
 22        ///  缓存锁
 23        ///   </summary>
 24       private   object  _Lock;
 25  
 26       #endregion
 27  
 28       #region  公共属性
 29  
 30       ///   <summary>
 31        ///  换成对象个数
 32        ///   </summary>
 33       public   int  Count
 34      {
 35           get
 36          {
 37               return   this ._Dic.Count;
 38          }
 39      }
 40  
 41       ///   <summary>
 42        ///  列表数据
 43        ///   </summary>
 44       public  List<TData> List
 45      {
 46           get
 47          {
 48               if  ( this ._List.Count <  this ._Dic.Count)
 49              {
 50                   this ._List.Clear();
 51                   foreach  (KeyValuePair<TKey, TData> kvp  in   this ._Dic)
 52                  {
 53                       this ._List.Add(kvp.Value);
 54                  }
 55              }
 56               return   this ._List;
 57          }
 58      }
 59  
 60       #endregion
 61  
 62       #region  构造函数
 63  
 64       ///   <summary>
 65        ///  默认构造
 66        ///   </summary>
 67       public  ConcurrentDictionary()
 68      {
 69           this ._Dic =  new  Dictionary<TKey, TData>();
 70           this ._List =  new  List<TData>();
 71           this ._Lock =  new   object ();
 72      }
 73  
 74       ///   <summary>
 75        ///  设置源数据获取器
 76        ///   </summary>
 77        ///   <param name="sourceDataGetter"> 单一源数据(GetOneDataByOneKey)获取器 </param>
 78       public  ConcurrentDictionary(Func<TKey, TData> sourceDataGetter)
 79      {
 80           if  (sourceDataGetter ==  null )  throw   new  ArgumentNullException( " sourceDataGetter " );
 81           this ._SourceDataGetter = sourceDataGetter;
 82           this ._Dic =  new  Dictionary<TKey, TData>();
 83           this ._List =  new  List<TData>();
 84           this ._Lock =  new   object ();
 85      }
 86  
 87       ///   <summary>
 88        ///  设置源数据获取器
 89        ///   </summary>
 90        ///   <param name="sourceAllDataGetter"> 所有源数据获取器 </param>
 91       public  ConcurrentDictionary(Func<List<TData>> sourceAllDataGetter)
 92      {
 93           if  (sourceAllDataGetter ==  null )  throw   new  ArgumentNullException( " sourceAllDataGetter " );
 94           this ._SourceAllDataGetter = sourceAllDataGetter;
 95           this ._Dic =  new  Dictionary<TKey, TData>();
 96           this ._List = sourceAllDataGetter();
 97           this ._Lock =  new   object ();
 98      }
 99  
100       #endregion
101  
102       #region  公共方法
103  
104       ///   <summary>
105        ///  获取缓存数据
106        ///   <para>  如果缓存中不存在,则通过SourceGetter获取 </para>
107        ///   <para>  如果通过SourceGetter获取到null对象,则不添加到缓存 </para>
108        ///   </summary>
109        ///   <param name="key"> 键名 </param>
110        ///   <param name="value"> 键值 </param>
111        ///   <returns> 返回是否获取成功 </returns>
112       public   bool  Get(TKey key,  out   TData value)
113      {
114           if  (_Dic.TryGetValue(key,  out  value))  return   true ;
115           else
116          {
117               lock  (_Lock)
118              {
119                   if  (_Dic.TryGetValue(key,  out  value))  return   true ;
120                   if  (_SourceDataGetter ==  null )  return   false ;
121                  TData tempData = _SourceDataGetter(key);
122                   if  (tempData !=  null )
123                  {
124                      _Dic.Add(key, tempData);
125                      _List.Add(tempData);
126                      value = tempData;
127                       return   true ;
128                  }
129                   return   false ;
130              }
131          }
132      }
133  
134       ///   <summary>
135        ///  设置缓存数据
136        ///   </summary>
137        ///   <param name="key"> 键名 </param>
138        ///   <param name="value"> 键值 </param>
139        ///   <returns> 返回是否设置成功,如果键值已存在,则返回false </returns>
140       public   bool  Add(TKey key, TData value)
141      {
142           if  (_Dic.ContainsKey(key))  return   false ;
143           else
144          {
145               lock  (_Lock)
146              {
147                   if  (_Dic.ContainsKey(key))  return   false ;
148                   else
149                  {
150                      _Dic.Add(key, value);
151                       if  (! this ._List.Contains(value))  this ._List.Add(value);
152                       return   true ;
153                  }
154              }
155          }
156      }
157  
158       ///   <summary>
159        ///  设置缓存数据
160        ///   </summary>
161        ///   <param name="key"> 键名 </param>
162        ///   <param name="value"> 键值 </param>
163        ///   <returns> 返回是否设置成功,如果键值已存在,则覆盖 </returns>
164       public   bool  Set(TKey key, TData value)
165      {
166           if  (_Dic.ContainsKey(key))
167          {
168               lock  ( this ._Lock)
169              {
170                   // 移除老数据
171                  TData oldData = _Dic[key];
172                  _List.Remove(oldData);
173                   // 增加新数据
174                  _Dic[key] = value;
175                  _List.Add(value);
176                   return   true ;
177              }
178          }
179           else
180          {
181               lock  (_Lock)
182              {
183                   if  (_Dic.ContainsKey(key))
184                  {
185                       // 移除老数据
186                      TData oldData = _Dic[key];
187                      _List.Remove(oldData);
188                       // 增加新数据
189                      _Dic[key] = value;
190                      _List.Add(value);
191                       return   true ;
192                  }
193                   else
194                  {
195                      _Dic.Add(key, value);
196                       if  (! this ._List.Contains(value))  this ._List.Add(value);
197                       return   true ;
198                  }
199              }
200          }
201      }
202  
203       ///   <summary>
204        ///  通过SourceDataGetter重新加载指定key的值
205        ///   </summary>
206        ///   <param name="key"> 键值 </param>
207        ///   <returns></returns>
208       public   bool  Reload(TKey key)
209      {
210           if  (_SourceDataGetter ==  null )  return   false ;
211          TData tempData = _SourceDataGetter(key);
212           return   this .Set(key, tempData);
213      }
214  
215       ///   <summary>
216        ///  通过SourceAllDataGetter重新加载所有缓存对象
217        ///   </summary>
218        ///   <returns></returns>
219       public   bool  ReloadAll()
220      {
221           if  (_SourceAllDataGetter ==  null )  return   false ;
222           lock  ( this ._Lock)
223          {
224               this ._List = _SourceAllDataGetter();
225          }
226           return   true ;
227      }
228  
229       ///   <summary>
230        ///  移除键/值
231        ///   </summary>
232        ///   <param name="key"> 键名 </param>
233        ///   <returns> 返回是否移除成功,如果不存在,则返回false </returns>
234       public   bool  Remove(TKey key)
235      {
236          TData tempData;
237           if  ( this ._Dic.TryGetValue(key,  out  tempData))
238          {
239               lock  (_Lock)
240              {
241                  _Dic.Remove(key);
242                  _List.Remove(tempData);
243                   return   true ;
244              }
245          }
246           return   false ;
247      }
248  
249       ///   <summary>
250        ///  清空缓存
251        ///   </summary>
252       public   void  Clear()
253      {
254           lock  (_Lock)
255          {
256              _Dic.Clear();
257              _List.Clear();
258          }
259      }
260  
261       #endregion

262  } 

      代码中的注释写的相对比较详细了,所以怎么用这里就不作任何多余的解释了,如果只是上面这个类的话,还不足以满足上面列出的Dictionary的缺点的第一点,所以笔者另外写了一个缓存管理类,如下:

  1   public   static   class  CacheManager
  2  {
  3       #region  私有字段
  4  
  5       ///   <summary>
  6        ///  缓存器
  7        ///   </summary>
  8       static  Dictionary< int ,  object > _Dic;
  9  
 10       #endregion
 11  
 12       #region  私有方法
 13  
 14       static  CacheManager()
 15      {
 16          _Dic =  new  Dictionary< int ,  object >();
 17      }
 18       ///   <summary>
 19        ///  获取下一个缓存键名
 20        ///   </summary>
 21        ///   <returns></returns>
 22       private   static   int  _GetNextKey()
 23      {
 24           int  key = Common.Random.Next( 1 ,  1000000 );
 25           while  (_Dic.ContainsKey(key))
 26          {
 27              key =  new  Random().Next( 1 ,  1000000 );
 28          }
 29           return  key;
 30      }
 31       ///   <summary>
 32        ///  注册字典
 33        ///   </summary>
 34        ///   <typeparam name="TKey"> 缓存键名类型 </typeparam>
 35        ///   <typeparam name="TData"> 缓存键值类型 </typeparam>
 36        ///   <param name="dic"> 缓存 </param>
 37        ///   <returns> 缓存类别键名 </returns>
 38       private   static   int  _Register<TKey, TData>(ConcurrentDictionary<TKey, TData> dic)
 39      {
 40           int  key = _GetNextKey();
 41          _Dic.Add(key, dic);
 42           return  key;
 43      }
 44  
 45       #endregion
 46  
 47       #region  公共方法
 48  
 49       ///   <summary>
 50        ///  注册缓存,并返回缓存键值
 51        ///   </summary>
 52        ///   <typeparam name="TKey"> 缓存键名类型 </typeparam>
 53        ///   <typeparam name="TData"> 缓存键值类型 </typeparam>
 54        ///   <returns> 缓存类别键名 </returns>
 55       public   static   int  Register<TKey, TData>()
 56      {
 57           return  _Register( new  ConcurrentDictionary<TKey, TData>());
 58      }
 59  
 60       ///   <summary>
 61        ///  注册缓存,并返回缓存键值
 62        ///   </summary>
 63        ///   <typeparam name="TKey"> 缓存键名类型 </typeparam>
 64        ///   <typeparam name="TData"> 缓存键值类型 </typeparam>
 65        ///   <param name="sourceDataGetter"> 单一源数据(GetOneDataByOneKey)获取器 </param>
 66        ///   <returns> 缓存类别键名 </returns>
 67       public   static   int  Register<TKey, TData>(Func<TKey, TData> sourceDataGetter)
 68      {
 69           return  _Register( new  ConcurrentDictionary<TKey, TData>(sourceDataGetter));
 70      }
 71  
 72       ///   <summary>
 73        ///  注册缓存,并返回缓存键值
 74        ///   </summary>
 75        ///   <typeparam name="TKey"> 缓存键名类型 </typeparam>
 76        ///   <typeparam name="TData"> 缓存键值类型 </typeparam>
 77        ///   <param name="sourceAllDataGetter"> 所有源数据获取器 </param>
 78        ///   <returns> 缓存类别键名 </returns>
 79       public   static   int  Register<TKey, TData>(Func<List<TData>> sourceAllDataGetter)
 80      {
 81           return  _Register( new  ConcurrentDictionary<TKey, TData>(sourceAllDataGetter));
 82      }
 83  
 84       ///   <summary>
 85        ///  获取缓存数据
 86        ///   <para>  如果缓存中不存在,则通过SourceGetter获取 </para>
 87        ///   <para>  如果通过SourceGetter获取到null对象,则不添加到缓存 </para>
 88        ///   </summary>
 89        ///   <typeparam name="TKey"> 缓存键名类型 </typeparam>
 90        ///   <typeparam name="TData"> 缓存键值类型 </typeparam>
 91        ///   <param name="cacheTypeKey"> 缓存类别键名 </param>
 92        ///   <param name="key"> 键名 </param>
 93        ///   <param name="value"> 键值 </param>
 94        ///   <returns> 返回是否获取成功 </returns>
 95       public   static   bool  Get<TKey, TData>( int  cacheTypeKey, TKey key,  out   TData value)
 96      {
 97           object  obj;
 98           if  (_Dic.TryGetValue(cacheTypeKey,  out  obj))
 99          {
100              ConcurrentDictionary<TKey, TData> dic = obj  as  ConcurrentDictionary<TKey, TData>;
101               return  dic.Get(key,  out  value);
102          }
103          value =  default (TData);
104           return   false ;
105      }
106  
107       ///   <summary>
108        ///  设置缓存数据
109        ///   </summary>
110        ///   <typeparam name="TKey"> 缓存键名类型 </typeparam>
111        ///   <typeparam name="TData"> 缓存键值类型 </typeparam>
112        ///   <param name="cacheTypeKey"> 缓存类别键名 </param>
113        ///   <param name="key"> 键名 </param>
114        ///   <param name="value"> 键值 </param>
115        ///   <returns> 返回是否设置成功,如果键值已存在,则返回false </returns>
116       public   static   bool  Add<TKey, TData>( int  cacheTypeKey, TKey key, TData value)
117      {
118           object  obj;
119           if  (_Dic.TryGetValue(cacheTypeKey,  out  obj))
120          {
121              ConcurrentDictionary<TKey, TData> dic = obj  as  ConcurrentDictionary<TKey, TData>;
122               return  dic.Add(key, value);
123          }
124           return   false ;
125      }
126  
127       ///   <summary>
128        ///  设置缓存数据
129        ///   </summary>
130        ///   <typeparam name="TKey"> 缓存键名类型 </typeparam>
131        ///   <typeparam name="TData"> 缓存键值类型 </typeparam>
132        ///   <param name="cacheTypeKey"> 缓存类别键名 </param>
133        ///   <param name="key"> 键名 </param>
134        ///   <param name="value"> 键值 </param>
135        ///   <returns> 返回是否设置成功,如果键值已存在,则覆盖 </returns>
136       public   static   bool  Set<TKey, TData>( int  cacheTypeKey, TKey key, TData value)
137      {
138           object  obj;
139           if  (_Dic.TryGetValue(cacheTypeKey,  out  obj))
140          {
141              ConcurrentDictionary<TKey, TData> dic = obj  as  ConcurrentDictionary<TKey, TData>;
142               return  dic.Set(key, value);
143          }
144           return   false ;
145      }
146  
147       ///   <summary>
148        ///  通过SourceDataGetter重新加载指定key的值
149        ///   </summary>
150        ///   <typeparam name="TKey"> 缓存键名类型 </typeparam>
151        ///   <typeparam name="TData"> 缓存键值类型 </typeparam>
152        ///   <param name="cacheTypeKey"> 缓存类别键名 </param>
153        ///   <param name="key"> 键值 </param>
154        ///   <returns></returns>
155       public   static   bool  Reload<TKey, TData>( int  cacheTypeKey, TKey key)
156      {
157           object  obj;
158           if  (_Dic.TryGetValue(cacheTypeKey,  out  obj))
159          {
160              ConcurrentDictionary<TKey, TData> dic = obj  as  ConcurrentDictionary<TKey, TData>;
161               return  dic.Reload(key);
162          }
163           return   false ;
164      }
165  
166       ///   <summary>
167        ///  通过SourceAllDataGetter重新加载指定类型缓存的所有缓存对象
168        ///   </summary>
169        ///   <typeparam name="TKey"> 缓存键名类型 </typeparam>
170        ///   <typeparam name="TData"> 缓存键值类型 </typeparam>
171        ///   <param name="cacheTypeKey"> 缓存类别键名 </param>
172        ///   <returns></returns>
173       public   static   bool  Reload<TKey, TData>( int  cacheTypeKey)
174      {
175           object  obj;
176           if  (_Dic.TryGetValue(cacheTypeKey,  out  obj))
177          {
178              ConcurrentDictionary<TKey, TData> dic = obj  as  ConcurrentDictionary<TKey, TData>;
179               return  dic.ReloadAll();
180          }
181           return   false ;
182      }
183  
184       ///   <summary>
185        ///  移除键/值
186        ///   </summary>
187        ///   <typeparam name="TKey"> 缓存键名类型 </typeparam>
188        ///   <typeparam name="TData"> 缓存键值类型 </typeparam>
189        ///   <param name="cacheTypeKey"> 缓存类别键名 </param>
190        ///   <param name="key"> 键名 </param>
191        ///   <returns> 返回是否移除成功,如果不存在,则返回false </returns>
192       public   static   bool  Remove<TKey, TData>( int  cacheTypeKey, TKey key)
193      {
194           object  obj;
195           if  (_Dic.TryGetValue(cacheTypeKey,  out  obj))
196          {
197              ConcurrentDictionary<TKey, TData> dic = obj  as  ConcurrentDictionary<TKey, TData>;
198               return  dic.Remove(key);
199          }
200           return   false ;
201      }
202  
203       ///   <summary>
204        ///  清空缓存
205        ///   </summary>
206        ///   <typeparam name="TKey"> 缓存键名类型 </typeparam>
207        ///   <typeparam name="TData"> 缓存键值类型 </typeparam>
208        ///   <param name="cacheTypeKey"> 缓存类别键名 </param>
209       public   static   void  Clear<TKey, TData>( int  cacheTypeKey)
210      {
211           object  obj;
212           if  (_Dic.TryGetValue(cacheTypeKey,  out  obj))
213          {
214              ConcurrentDictionary<TKey, TData> dic = obj  as  ConcurrentDictionary<TKey, TData>;
215              dic.Clear();
216          }
217      }
218  
219       ///   <summary>
220        ///  清空所有缓存
221        ///   </summary>
222       public   static   void  ClearAll()
223      {
224          _Dic.Clear();
225      }
226  
227       #endregion

228  } 

       以上两个类 就是我的缓存管理的全部实现了,谢谢!

ASP.NET开发技术交流群: 67511751

另:本人想找一些志同道合的人,可以是跟我一起交流技术的,或者是给予鼓励和支持的,非诚勿扰,谢谢!

QQ:1054930154  

 

分类:  ASP.NET-相关 ,  面向对象程序开发

标签:  缓存 缓存管理 多线程 字典

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于浅谈缓存管理的详细内容...

  阅读:33次