缓存这个东西可大可小,小到一个静态的字段,大到将整个数据库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 == null) throw 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 == null) throw 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 == 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
代码中的注释写的相对比较详细了,所以怎么用这里就不作任何多余的解释了,如果只是上面这个类的话,还不足以满足上面列出的Dictionary的缺点的第一点,所以笔者另外写了一个缓存管理类,如下:
001.
public static class CacheManager
002.
{
003.
#region 私有字段
004.
005.
/// <summary>
006.
/// 缓存器
007.
/// </summary>
008.
static Dictionary<int, object> _Dic;
009.
010.
#endregion
011.
012.
#region 私有方法
013.
014.
static CacheManager()
015.
{
016.
_Dic = new Dictionary<int, object>();
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
以上两个类 就是我的缓存管理的全部实现了,谢谢!
本文介绍了一种基于C#的缓存管理实现方案,利用ConcurrentDictionary解决线程安全问题,并通过CacheManager类实现缓存注册及管理,有效提高了应用性能。
771

被折叠的 条评论
为什么被折叠?



