C#浅谈数据缓存管理

缓存这个东西可大可小,小到一个静态的字段,大到将整个数据库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的线程安全问题www.it165.net;
这点不多说,因为Dictionary不是线程安全的。

Dictionary的与实际数据的同步问题;
一般来说,既然用到缓存,那么这些数据基本上是读操作远远大于写操作的,所以不可能为了那么一点点的写操作,来额外加多一个定时器进行数据同步,如果是这样的话,如果有100类数据需要进行缓存,那不是伴随着100个定时。,难以想想,这是多么大的一个累赘啊。

      请别上火,因为写了这么多还在说理论;在写我自己的缓存实现的时候,我首先要谈谈我这个缓存管理的应用场景,因为任何一项技术都是因为特定需求而产生的,脱离了实际需求的技术都是空谈,说白了就是无用功。
      在我的项目中,有很多的基础对象,如果数据字典,枚举,模块,权限等数据库对象,这些信息是在项目开发完成后基本上是不会再发生太大的改变的,而且这些数据的访问平率是非常高的,如果每次访问都需要一次数据库连接的话,那对数据库的压力可想而知了。下面是ConcurrentDictionary.cs的代码:

 

001. public class ConcurrentDictionary<TKey, TData>
002. {
003. #region 私有字段
004.  
005. /// <summary>
006. /// 单一源数据(GetOneDataByOneKey)获取器
007. /// </summary>
008. private Func<TKey, TData> _SourceDataGetter;
009. /// <summary>
010. /// 所有源数据获取器
011. /// </summary>
012. private Func<List<TData>> _SourceAllDataGetter;
013. /// <summary>
014. /// 缓存存放字典对象
015. /// </summary>
016. private Dictionary<TKey, TData> _Dic;
017. /// <summary>
018. /// 缓存存放列表对象
019. /// </summary>
020. private List<TData> _List;
021. /// <summary>
022. /// 缓存锁
023. /// </summary>
024. private object _Lock;
025.  
026. #endregion
027.  
028. #region 公共属性
029.  
030. /// <summary>
031. /// 换成对象个数
032. /// </summary>
033. public int Count
034. {
035. get
036. {
037. return this._Dic.Count;
038. }
039. }
040.  
041. /// <summary>
042. /// 列表数据
043. /// </summary>
044. public List<TData> List
045. {
046. get
047. {
048. if (this._List.Count < this._Dic.Count)
049. {
050. this._List.Clear();
051. foreach (KeyValuePair<TKey, TData> kvp in this._Dic)
052. {
053. this._List.Add(kvp.Value);
054. }
055. }
056. return this._List;
057. }
058. }
059.  
060. #endregion
061.  
062. #region 构造函数
063.  
064. /// <summary>
065. /// 默认构造
066. /// </summary>
067. public ConcurrentDictionary()
068. {
069. this._Dic = new Dictionary<TKey, TData>();
070. this._List = new List<TData>();
071. this._Lock = new object();
072. }
073.  
074. /// <summary>
075. /// 设置源数据获取器
076. /// </summary>
077. /// <param name="sourceDataGetter">单一源数据(GetOneDataByOneKey)获取器</param>
078. public ConcurrentDictionary(Func<TKey, TData> sourceDataGetter)
079. {
080. if (sourceDataGetter == nullthrow newArgumentNullException("sourceDataGetter");
081. this._SourceDataGetter = sourceDataGetter;
082. this._Dic = new Dictionary<TKey, TData>();
083. this._List = new List<TData>();
084. this._Lock = new object();
085. }
086.  
087. /// <summary>
088. /// 设置源数据获取器
089. /// </summary>
090. /// <param name="sourceAllDataGetter">所有源数据获取器</param>
091. public ConcurrentDictionary(Func<List<TData>> sourceAllDataGetter)
092. {
093. if (sourceAllDataGetter == nullthrow newArgumentNullException("sourceAllDataGetter");
094. this._SourceAllDataGetter = sourceAllDataGetter;
095. this._Dic = new Dictionary<TKey, TData>();
096. this._List = sourceAllDataGetter();
097. this._Lock = new object();
098. }
099.  
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

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

 

001. public static class CacheManager
002. {
003. #region 私有字段
004.  
005. /// <summary>
006. /// 缓存器
007. /// </summary>
008. static Dictionary<intobject> _Dic;
009.  
010. #endregion
011.  
012. #region 私有方法
013.  
014. static CacheManager()
015. {
016. _Dic = new Dictionary<intobject>();
017. }
018. /// <summary>
019. /// 获取下一个缓存键名
020. /// </summary>
021. /// <returns></returns>
022. private static int _GetNextKey()
023. {
024. int key = Common.Random.Next(1, 1000000);
025. while (_Dic.ContainsKey(key))
026. {
027. key = new Random().Next(1, 1000000);
028. }
029. return key;
030. }
031. /// <summary>
032. /// 注册字典
033. /// </summary>
034. /// <typeparam name="TKey">缓存键名类型</typeparam>
035. /// <typeparam name="TData">缓存键值类型</typeparam>
036. /// <param name="dic">缓存</param>
037. /// <returns>缓存类别键名</returns>
038. private static int _Register<TKey, TData>(ConcurrentDictionary<TKey, TData> dic)
039. {
040. int key = _GetNextKey();
041. _Dic.Add(key, dic);
042. return key;
043. }
044.  
045. #endregion
046.  
047. #region 公共方法
048.  
049. /// <summary>
050. /// 注册缓存,并返回缓存键值
051. /// </summary>
052. /// <typeparam name="TKey">缓存键名类型</typeparam>
053. /// <typeparam name="TData">缓存键值类型</typeparam>
054. /// <returns>缓存类别键名</returns>
055. public static int Register<TKey, TData>()
056. {
057. return _Register(new ConcurrentDictionary<TKey, TData>());
058. }
059.  
060. /// <summary>
061. /// 注册缓存,并返回缓存键值
062. /// </summary>
063. /// <typeparam name="TKey">缓存键名类型</typeparam>
064. /// <typeparam name="TData">缓存键值类型</typeparam>
065. /// <param name="sourceDataGetter">单一源数据(GetOneDataByOneKey)获取器</param>
066. /// <returns>缓存类别键名</returns>
067. public static int Register<TKey, TData>(Func<TKey, TData> sourceDataGetter)
068. {
069. return _Register(new ConcurrentDictionary<TKey, TData>(sourceDataGetter));
070. }
071.  
072. /// <summary>
073. /// 注册缓存,并返回缓存键值
074. /// </summary>
075. /// <typeparam name="TKey">缓存键名类型</typeparam>
076. /// <typeparam name="TData">缓存键值类型</typeparam>
077. /// <param name="sourceAllDataGetter">所有源数据获取器</param>
078. /// <returns>缓存类别键名</returns>
079. public static int Register<TKey, TData>(Func<List<TData>> sourceAllDataGetter)
080. {
081. return _Register(new ConcurrentDictionary<TKey, TData>(sourceAllDataGetter));
082. }
083.  
084. /// <summary>
085. /// 获取缓存数据
086. /// <para> 如果缓存中不存在,则通过SourceGetter获取</para>
087. /// <para> 如果通过SourceGetter获取到null对象,则不添加到缓存</para>
088. /// </summary>
089. /// <typeparam name="TKey">缓存键名类型</typeparam>
090. /// <typeparam name="TData">缓存键值类型</typeparam>
091. /// <param name="cacheTypeKey">缓存类别键名</param>
092. /// <param name="key">键名</param>
093. /// <param name="value">键值</param>
094. /// <returns>返回是否获取成功</returns>
095. public static bool Get<TKey, TData>(int cacheTypeKey, TKey key, out  TData value)
096. {
097. object obj;
098. if (_Dic.TryGetValue(cacheTypeKey, out obj))
099. {
100. ConcurrentDictionary<TKey, TData> dic = obj asConcurrentDictionary<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 asConcurrentDictionary<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 asConcurrentDictionary<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 asConcurrentDictionary<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 asConcurrentDictionary<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 asConcurrentDictionary<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 asConcurrentDictionary<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

以上两个类 就是我的缓存管理的全部实现了,谢谢!
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值