缓存过期策略
如果数据库管理员来删除了数据呢,会存在缓存里面的数据不可用!缓存出现脏数据库;这是无法完全避免的;
什么情况下使用缓存?
二八原则 查询频繁;修改少!
一般有如下三种策略:
永不过期
- 一直能够使用
绝对过期
- 过了多长时间以后,就过期了 就不能用了,此时可以直接将数据删除
滑动过期
- 设定好过期时间后,如果在有效期内使用过,就往后滑动 比如说,当前8点,设置到8点半过期,但是8点10分的时候使用过这个数据了,过期时间会滑动半小时,就是说8点40才会过期
下面的缓存我们是通过字典自定义的, 如果存在多线程最好加锁:
代码:
public class CustomCache { static CustomCache() //CLR调用 整个进程执行且只执行一次 { Task.Run(() => // { while (true) //死循环来判断 { try { //Thread.Sleep(60 * 1000 * 10); //十分钟后开始清理缓存 List<string> delKeyList = new List<string>(); lock (obj_Lock) { foreach (var key in CustomCacheDictionary.Keys) { DataModel model = CustomCacheDictionary[key]; if (model.Deadline < DateTime.Now && model.ObsloteType != ObsloteType.Never) // { delKeyList.Add(key); } } delKeyList.ForEach(key => Remove(key)); } } catch (Exception ex) { Console.WriteLine(ex.Message); throw; } } }); } /// <summary> /// static:不会被Gc回收; /// Private:不让外部访问他 /// </summary> private static Dictionary<string, DataModel> CustomCacheDictionary = new Dictionary<string, DataModel>(); private static readonly object obj_Lock = new object(); /// <summary> /// 默认你是不过期 /// </summary> /// <param name="key"></param> /// <param name="value"></param> public static void Add(string key, object value) { lock (obj_Lock) CustomCacheDictionary.Add(key, new DataModel() { Value = value, ObsloteType = ObsloteType.Never }); } /// <summary> /// 绝对过期 /// </summary> /// <param name="key"></param> /// <param name="value"></param> /// <param name="timeOutSecond"></param> public static void Add(string key, object value, int timeOutSecond) //3000 { lock (obj_Lock) CustomCacheDictionary.Add(key, new DataModel() { Value = value, ObsloteType = ObsloteType.Absolutely, Deadline = DateTime.Now.AddSeconds(timeOutSecond) }); ; } public static void Add(string key, object value, TimeSpan durtion) { lock (obj_Lock) CustomCacheDictionary.Add(key, new DataModel() { Value = value, ObsloteType = ObsloteType.Relative, Deadline = DateTime.Now.Add(durtion), Duraton = durtion }); ; ; } //清楚所有缓存,殃及池鱼! public static void RemoveAll() { lock (obj_Lock) CustomCacheDictionary.Clear();//字典中的所有内容全部被清理到 } public static void Remove(string key) { lock (obj_Lock) CustomCacheDictionary.Remove(key); } /// <summary> /// 按条件删除 /// </summary> /// <param name="func"></param> public static void RemoveCondition(Func<string, bool> func) { List<string> keyList = new List<string>(); lock (obj_Lock) foreach (var key in CustomCacheDictionary.Keys) { if (func.Invoke(key)) { keyList.Add(key); } } keyList.ForEach(s => Remove(s)); } public static T Get<T>(string key) { return (T)(CustomCacheDictionary[key]).Value; } /// <summary> /// 判断是否存在 /// 判断是哪种过期策略,并且还要看是否过期,如果过期的话还要将数据删除 /// 不过期的话 滑动过期的还将处理过期时间 /// </summary> /// <param name="key"></param> /// <returns></returns> public static bool Exists(string key) { if (CustomCacheDictionary.ContainsKey(key)) { DataModel model = CustomCacheDictionary[key]; if (model.ObsloteType == ObsloteType.Never) { return true; } else if (model.Deadline < DateTime.Now) // { lock (obj_Lock) { CustomCacheDictionary.Remove(key); return false; } } else { if (model.ObsloteType == ObsloteType.Relative) { model.Deadline = DateTime.Now.Add(model.Duraton); } return true; } } else { return false; } } public static T GetT<T>(string key, Func<T> func) { T t = default(T); if (!Exists(key)) { t = func.Invoke(); Add(key, t); } else { t = Get<T>(key); } return t; } }
如果不想加锁,可以使用ConcurrentDictionary,会保证表示可由多个线程同时访问的键/值对的线程安全集合。
代码示例:
public class CustomCacheNew { static CustomCacheNew() // { Task.Run(() => // { while (true) //死循环来判断 { try { //Thread.Sleep(60 * 1000 * 10); //十分钟后开始清理缓存 List<string> delKeyList = new List<string>(); foreach (var key in CustomCacheDictionary.Keys) { DataModel model = CustomCacheDictionary[key]; if (model.Deadline < DateTime.Now && model.ObsloteType != ObsloteType.Never) // { delKeyList.Add(key); } } delKeyList.ForEach(key => Remove(key)); } catch (Exception ex) { Console.WriteLine(ex.Message); throw; } } }); } /// <summary> /// static:不会被Gc回收; /// Private:不让外部访问他 /// /// 线程安全字典 /// </summary> private static ConcurrentDictionary<string, DataModel> CustomCacheDictionary = new ConcurrentDictionary<string, DataModel>(); /// <summary> /// 默认你是不过期 /// </summary> /// <param name="key"></param> /// <param name="value"></param> public static void Add(string key, object value) { CustomCacheDictionary.TryAdd(key, new DataModel() { Value = value, ObsloteType = ObsloteType.Never }); } /// <summary> /// 绝对过期 /// </summary> /// <param name="key"></param> /// <param name="value"></param> /// <param name="timeOutSecond"></param> public static void Add(string key, object value, int timeOutSecond) //3000 { CustomCacheDictionary.TryAdd(key, new DataModel() { Value = value, ObsloteType = ObsloteType.Absolutely, Deadline = DateTime.Now.AddSeconds(timeOutSecond) }); ; } public static void Add(string key, object value, TimeSpan durtion) { CustomCacheDictionary.TryAdd(key, new DataModel() { Value = value, ObsloteType = ObsloteType.Relative, Deadline = DateTime.Now.Add(durtion), Duraton = durtion }); ; ; } //清楚所有缓存,殃及池鱼! public static void RemoveAll() { CustomCacheDictionary.Clear();//字典中的所有内容全部被清理到 } public static void Remove(string key) { DataModel data = null; CustomCacheDictionary.TryRemove(key, out data); } public static T Get<T>(string key) { return (T)(CustomCacheDictionary[key]).Value; } /// <summary> /// 判断是否存在 /// </summary> /// <param name="key"></param> /// <returns></returns> public static bool Exists(string key) { if (CustomCacheDictionary.ContainsKey(key)) { DataModel model = CustomCacheDictionary[key]; if (model.ObsloteType == ObsloteType.Never) { return true; } else if (model.Deadline < DateTime.Now) // { DataModel data = null; CustomCacheDictionary.TryRemove(key, out data); return false; } else { if (model.ObsloteType == ObsloteType.Relative) { model.Deadline = DateTime.Now.Add(model.Duraton); } return true; } } else { return false; } } public static T GetT<T>(string key, Func<T> func) { T t = default(T); if (!Exists(key)) { t = func.Invoke(); Add(key, t); } else { t = Get<T>(key); } return t; } }
一般情况下我们都是使用字典加锁来实现,但是加锁的话就会造成性能上的问题,我们可以通过模拟CPU分片的方式来解决,这样锁的时候就锁了这一个字典缓存,另外几个字典不会被锁住
代码:
public class CustomCacheNewRichard { private static List<Dictionary<string, DataModel>> dicCacheList = new List<Dictionary<string, DataModel>>(); private static List<object> lockList = new List<object>(); public static int CupNum = 0; static CustomCacheNewRichard() // { CupNum = 3;//模拟获取获取CPU片数 //动态生成字典 for (int i = 0; i < CupNum; i++) { dicCacheList.Add(new Dictionary<string, DataModel>()); //CPU 有几片 就来几个字典 lockList.Add(new object());//每个字典对应一个锁 } Task.Run(() => // { while (true) //死循环来判断 { try { for (int i = 0; i < CupNum; i++) { lock (lockList[i]) { //Thread.Sleep(60 * 1000 * 10); //十分钟后开始清理缓存 List<string> delKeyList = new List<string>(); foreach (var key in dicCacheList[i].Keys) { DataModel model = dicCacheList[i][key]; if (model.Deadline < DateTime.Now && model.ObsloteType != ObsloteType.Never) // { delKeyList.Add(key); } } delKeyList.ForEach(key => dicCacheList[i].Remove(key)); } } } catch (Exception ex) { Console.WriteLine(ex.Message); throw; } } }); } /// <summary> /// 默认你是不过期 /// </summary> /// <param name="key"></param> /// <param name="value"></param> public static void Add(string key, object value) { int hash = key.GetHashCode() * (-1); //只要字符串不变,hash值不变! int index = hash % CupNum; lock (lockList[index]) dicCacheList[index].Add(key, new DataModel() { Value = value, ObsloteType = ObsloteType.Never }); } /// <summary> /// 绝对过期 /// </summary> /// <param name="key"></param> /// <param name="value"></param> /// <param name="timeOutSecond"></param> public static void Add(string key, object value, int timeOutSecond) //3000 { int hash = key.GetHashCode() * (-1); //只要字符串变,hash值不变! int index = hash % CupNum; lock (lockList[index]) dicCacheList[index].Add(key, new DataModel() { Value = value, ObsloteType = ObsloteType.Absolutely, Deadline = DateTime.Now.AddSeconds(timeOutSecond) }); ; } public static void Add(string key, object value, TimeSpan durtion) { int hash = key.GetHashCode() * (-1); //只要字符串变,hash值不变! int index = hash % CupNum; lock (lockList[index]) dicCacheList[index].Add(key, new DataModel() { Value = value, ObsloteType = ObsloteType.Relative, Deadline = DateTime.Now.Add(durtion), Duraton = durtion }); ; ; } //清楚所有缓存,殃及池鱼! public static void RemoveAll() { for (int i = 0; i < CupNum; i++) { dicCacheList[i].Clear(); } } public static void Remove(string key) { int hash = key.GetHashCode() * (-1); //只要字符串变,hash值不变! int index = hash % CupNum; if (dicCacheList[index].ContainsKey(key)) { dicCacheList[index].Remove(key); } } public static T Get<T>(string key) { int hash = key.GetHashCode() * (-1); //只要字符串变,hash值不变! int index = hash % CupNum; return (T)(dicCacheList[index][key]).Value; } /// <summary> /// 判断是否存在 /// </summary> /// <param name="key"></param> /// <returns></returns> public static bool Exists(string key) { int hash = key.GetHashCode() * (-1); //只要字符串变,hash值不变! int index = hash % CupNum; if (dicCacheList[index].ContainsKey(key)) { DataModel model = dicCacheList[index][key]; if (model.ObsloteType == ObsloteType.Never) { return true; } else if (model.Deadline < DateTime.Now) // { dicCacheList[index].Remove(key); return false; } else { if (model.ObsloteType == ObsloteType.Relative) { model.Deadline = DateTime.Now.Add(model.Duraton); } return true; } } else { return false; } } public static T GetT<T>(string key, Func<T> func) { T t = default(T); if (!Exists(key)) { t = func.Invoke(); Add(key, t); } else { t = Get<T>(key); } return t; } }
上面的代码是自己定义的,我们可以使用系统框架自定义的MemoryCacheCache
MemoryCacheCache
它的本质其实也是类似于CPU分片实现的,封装的代码如下:
namespace Common { /// <summary> /// Cache manager interface /// </summary> public interface ICache { /// <summary> /// Gets or sets the value associated with the specified key. /// </summary> /// <typeparam name="T">Type</typeparam> /// <param name="key">The key of the value to get.</param> /// <returns>The value associated with the specified key.</returns> T Get<T>(string key); /// <summary> /// Adds the specified key and object to the cache. /// </summary> /// <param name="key">key</param> /// <param name="data">Data</param> /// <param name="cacheTime">Cache time</param> void Add(string key, object data, int cacheTime = 30); /// <summary> /// Gets a value indicating whether the value associated with the specified key is cached /// </summary> /// <param name="key">key</param> /// <returns>Result</returns> bool Contains(string key); /// <summary> /// Removes the value with the specified key from the cache /// </summary> /// <param name="key">/key</param> void Remove(string key); /// <summary> /// Clear all cache data /// </summary> void RemoveAll(); object this[string key] { get; set; } int Count { get; } } }
MemoryCacheCache
/// <summary> /// MemoryCacheCache /// </summary> public class MemoryCacheCache : ICache { public MemoryCacheCache() { } protected ObjectCache Cache { get { return MemoryCache.Default; } } /// <summary> /// 读取缓存 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <returns></returns> public T Get<T>(string key) { if (Cache.Contains(key)) { return (T)Cache[key]; } else { return default(T); } } public object Get(string key) { return Cache[key]; } /// <summary> /// 增加缓存 /// </summary> /// <param name="key"></param> /// <param name="data"></param> /// <param name="cacheTime">分钟</param> public void Add(string key, object data, int cacheTime = 30) { if (data == null) return; var policy = new CacheItemPolicy(); policy.AbsoluteExpiration = DateTime.Now + TimeSpan.FromMinutes(cacheTime); Cache.Add(new CacheItem(key, data), policy); } /// <summary> /// 是否包含 /// </summary> /// <param name="key"></param> /// <returns></returns> public bool Contains(string key) { return Cache.Contains(key); } public int Count { get { return (int)(Cache.GetCount()); } } /// <summary> /// 单个清除 /// </summary> /// <param name="key">/key</param> public void Remove(string key) { Cache.Remove(key); } /// <summary> /// 正则表达式移除 /// </summary> /// <param name="pattern">pattern</param> public void RemoveByPattern(string pattern) { var regex = new Regex(pattern, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.IgnoreCase); var keysToRemove = new List<String>(); foreach (var item in Cache) if (regex.IsMatch(item.Key)) keysToRemove.Add(item.Key); foreach (string key in keysToRemove) { Remove(key); } } /// <summary> /// 根据键值返回缓存数据 /// </summary> /// <param name="key"></param> /// <returns></returns> public object this[string key] { get { return Cache.Get(key); } set { Add(key, value); } } /// <summary> /// 清除全部数据 /// </summary> public void RemoveAll() { foreach (var item in Cache) Remove(item.Key); } }
CacheManager
public class CacheManager { #region Identity private CacheManager() { } private static ICache cache = null; static CacheManager() { Console.WriteLine("开始缓存的初始化....."); //可以创建不同的cache对象 cache = (ICache)Activator.CreateInstance(typeof(MemoryCacheCache)); // 这里可以根据配置文件来选择 //cache = (ICache)Activator.CreateInstance(typeof(CustomerCache)); } #endregion Identity #region ICache /// <summary> /// 当前缓存数据项的个数 /// </summary> public static int Count { get { return cache.Count; } } /// <summary> /// 如果缓存中已存在数据项键值,则返回true /// </summary> /// <param name="key">数据项键值</param> /// <returns>数据项是否存在</returns> public static bool Contains(string key) { return cache.Contains(key); } /// <summary> /// 获取缓存数据 /// </summary> /// <param name="key"></param> /// <returns></returns> public static T GetData<T>(string key) { return cache.Get<T>(key); } /// <summary> /// /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key">缓存的项</param> /// <param name="acquire">没有缓存的时候获取数据的方式</param> /// <param name="cacheTime">单位分钟 默认30</param> /// <returns></returns> public static T Get<T>(string key, Func<T> acquire, int cacheTime = 30) { if (!cache.Contains(key)) { T result = acquire.Invoke(); cache.Add(key, result, cacheTime); } return GetData<T>(key); //if (cache.Contains(key)) //{ // return GetData<T>(key); //} //else //{ // T result = acquire.Invoke();//执行委托 获取委托结果 作为缓存值 // cache.Add(key, result, cacheTime); // return result; //} } /// <summary> /// 添加缓存数据。 /// 如果另一个相同键值的数据已经存在,原数据项将被删除,新数据项被添加。 /// </summary> /// <param name="key">缓存数据的键值</param> /// <param name="value">缓存的数据,可以为null值</param> /// <param name="expiratTime">缓存过期时间间隔(单位:分钟)</param> public static void Add(string key, object value, int expiratTime = 30) { if (Contains(key)) cache.Remove(key); cache.Add(key, value, expiratTime); } /// <summary> /// 删除缓存数据项 /// </summary> /// <param name="key"></param> public static void Remove(string key) { cache.Remove(key); } /// <summary> /// 删除所有缓存数据项 /// </summary> public static void RemoveAll() { cache.RemoveAll(); } #endregion }