浅谈缓存管理

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

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

  1. Init():缓存的初始化;
    如:Dictionary<int, object> dic = new Dictioinary<int, object>();
  2. Add():增加缓存;
    如:dic.Add(1, new object());
  3. Set():设置缓存 ;
    这里的Set()和Add()是有一点区别的,Add()的时候发现已存在的缓存,则直接返回;而调用Set()的时候会检查,如果不存在执行Add();如果存在,则替换(Remove first,then add)。如:dic[1] = new object();
  4. Reload();重新加载缓存,在某些情况想功能相当于Init;
    如:dic=new Dictionary<int, object>();
  5. Remove():移除缓存;
    如:dic.Remove(1),(注:执行Remove方法的时候,类似于Set,当key不存在的时候,不作任何处理);
  6. Clear():清空缓存。
    如:dic.clear();
      如果你用过Dictionary对象的话,相信上面提到的方法对你都不会感到陌生,但是,仅仅一个Dictionary就能够满足我们的需要吗?这里笔者来说说Dictionary的缺点:

 

  1. 一个Dictionary对象的实例是不够用的;
    并非所有的缓存的key都是int类型,所以你会在需要的地方不断的定义Dictionary对象,管理起来到底有多难,你懂的。
  2. Dictionary的索引问题;
    大家都知道,在进行Dictionary[key]操作的时候,首先要判断是否存在。如果存在,则返回【或许你可以通过Dictionary.TryGetValue(...)方法来避开这一步检查】;如果不存在,则要通过一些方式来获取该对象,然后追加到缓存中去;
  3. Dictionary的遍历问题;
    或许你会讲,Dictionary是支持foreach遍历的,好吧,这个我也承认;但是,你有没有在有的时候,需要采用for的方式来遍历Dictioanry对象实例呢?那个时候我想你肯定也纠结了一番。
  4. Dictionary的查找问题;
    查找?不是可以通过key来索引吗,还需要什么查找?好吧,这个我也承认;但是有些时候,我不仅仅只是需要通过键名key来查找啊,可能还需要通过dic.Find<TValue>(Predicate match), dic.FindAll<TValue>(Predicate match)等方式来查找,这个Dictionary有吗?答案是:没有。
  5. Dictionary的线程安全问题;
    这点不多说,因为Dictionary不是线程安全的。
  6. 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 ==  nullthrow  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 ==  nullthrow  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 ==  nullreturn  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 ==  nullreturn  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 ==  nullreturn  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< intobject> _Dic;
  9 
 10      #endregion
 11 
 12      #region 私有方法
 13 
 14      static CacheManager()
 15     {
 16         _Dic =  new Dictionary< intobject>();
 17     }
 18      ///   <summary>
 19       ///  获取下一个缓存键名
 20       ///   </summary>
 21       ///   <returns></returns>
 22       private  static  int _GetNextKey()
 23     {
 24          int key = Common.Random.Next( 11000000);
 25          while (_Dic.ContainsKey(key))
 26         {
 27             key =  new Random().Next( 11000000);
 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 

 

 

posted on 2012-06-20 10:04 Juvy 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/Juvy/archive/2012/06/20/Concurrent-Dictionary-CacheManager.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值