Juvy

I Believe Persistence.

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

      缓存这个东西可大可小,小到一个静态的字段,大到将整个数据库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  阅读(4704)  评论(21编辑  收藏  举报
QQ:1054930154; Email:david.telvent@gmail.com; QQ群:67511751