上周将本地缓存切换到远程缓存后,导致系统运行缓慢,经分析是一个页面反复读取缓存数据。Reveiw代码,发现是开发人员对缓存调用不够规范,导致循环调用缓存。
代码遍布整个项目,修复成本较高,只能从底层的缓存框架解决。
经构思,觉得在远程缓存基础上增加本地缓存,默认本地缓存超时6秒,这样基本解决一次请求,相同的缓存反复请求远程缓存问题,修改如下:
1、请求缓存的时候,先请求本地缓存,如没有请求远程,远程有数据的时候,再本地缓存一份备份
2、设置缓存的时候,同时设置本地和远程缓存
3、省略本地缓存和远程缓存同步(因本地缓存设置过期时间非常短,只为了减少并发请求远程缓存,一般3-5秒左右)
缓存接口:
/// <summary> /// 缓存接口 /// </summary> public interface ICache { /// <summary> /// 从缓存中取得指定键的值 /// </summary> /// <param name="type">指定的类型</param> /// <param name="key">指定的键值</param> /// <returns></returns> object Get(Type type, string key); /// <summary> /// 从缓存中取得指定键的值 /// </summary> /// <typeparam name="T">类型</typeparam> /// <param name="key">指定的键值</param> /// <returns>指定键值的值</returns> T Get<T>(string key); /// <summary> /// 从缓存中取出对象,如果没有缓存调用acquire方法 /// </summary> /// <typeparam name="T">对象类型</typeparam> /// <param name="key">键值</param> /// <param name="acquire">如果缓存中没有对象的处理方法,处理方法</param> /// <param name="cacheTime">如果缓存中没有对象的处理方法设置缓存过期时间</param> /// <returns>指定键值的值</returns> T Get<T>(string key, Func<T> acquire, TimeSpan cacheTime); /// <summary> /// 从缓存中取出对象,如果没有缓存调用acquire方法 /// </summary> /// <typeparam name="T">对象类型</typeparam> /// <param name="key">键值</param> /// <param name="acquire">如果缓存中没有对象的处理方法,处理方法</param> /// <param name="expiredTime">缓存绝对过期时间</param> /// <returns>指定键值的值</returns> T Get<T>(string key, Func<T> acquire, DateTime expiredTime); /// <summary> /// 从缓存中取出对象,如果没有缓存调用acquire方法 /// </summary> /// <typeparam name="T">对象类型</typeparam> /// <param name="key">键值</param> /// <param name="acquire">如果缓存中没有对象的处理方法,处理方法</param> /// <param name="cacheExpiration">预先定义的缓存策略类型</param> /// <returns>指定键值的值</returns> T Get<T>(string key, Func<T> acquire, CacheExpirationTypes cacheExpiration); /// <summary> /// 添加一个对象到到缓存,使用缺省过期时间 /// </summary> /// <param name="key">键值</param> /// <param name="data">存储到缓存中的对象</param> /// <param name="dependencyKeys">父键值</param> void Set(string key, object data, params string[] dependencyKeys); /// <summary> /// 添加一个对象到到缓存 /// </summary> /// <param name="key">键值</param> /// <param name="data">存储到缓存中的对象</param> /// <param name="slidingTime">缓存时间</param> /// <param name="dependencyKeys">父键值</param> void Set(string key, object data, TimeSpan slidingTime, params string[] dependencyKeys); /// <summary> /// 添加一个绝对过期时间的对象到缓存 /// </summary> /// <param name="key">缓存键值</param> /// <param name="data">缓存对象</param> /// <param name="expiredTime">绝对过期时间</param> /// <param name="dependencyKeys">父键值</param> void Set(string key, object data, DateTime expiredTime, params string[] dependencyKeys); /// <summary> /// 添加一个对象到到缓存 /// </summary> /// <param name="key">键值</param> /// <param name="data">存储到缓存中的对象</param> /// <param name="cacheExpiration">缓存过期类型</param> /// <param name="dependencyKeys">父键值</param> void Set(string key, object data, CacheExpirationTypes cacheExpiration, params string[] dependencyKeys); /// <summary> /// 添加文件依赖对象到缓存 /// </summary> /// <param name="key">键值</param> /// <param name="value">存储到缓存中的对象</param> /// <param name="dependencyFiles">依赖文件项</param> void SetFile(string key, object value, params string[] dependencyFiles); /// <summary> /// 添加文件依赖对象到缓存 /// </summary> /// <param name="key">键值</param> /// <param name="value">存储到缓存中的对象</param> /// <param name="expiredTime">绝对过期时间</param> /// <param name="dependencyFiles">依赖文件项</param> void SetFile(string key, object value, DateTime expiredTime, params string[] dependencyFiles); /// <summary> /// 添加文件依赖对象到缓存 /// </summary> /// <param name="key">键值</param> /// <param name="value">存储到缓存中的对象</param> /// <param name="slidingTime">缓存持续时间</param> /// <param name="dependencyFiles">依赖文件项</param> void SetFile(string key, object value, TimeSpan slidingTime, params string[] dependencyFiles); /// <summary> /// 判断是否已经缓存 /// </summary> /// <param name="key">键值</param> /// <returns>是否存在</returns> bool IsSet(string key); /// <summary> /// 移除缓存 /// </summary> /// <param name="key">键值</param> void Remove(string key); /// <summary> /// 按模式移除缓存 /// </summary> /// <param name="pattern">正则表达式模式</param> void RemoveByPattern(string pattern); /// <summary> /// 清空缓存 /// </summary> void Clear(); }
修改的Redis缓存Get方法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /// <summary> 2 /// 从缓存中取得指定键的值 3 /// </summary> 4 /// <typeparam name="T">类型</typeparam> 5 /// <param name="key">指定的键值</param> 6 /// <returns>指定键值的值</returns> 7 public T Get<T>(string key) 8 { 9 key = GetKey(key); 10 if (_useLocal) 11 { 12 //先从本地获取缓存 13 var localValue = _localCache.Get(key); 14 if (localValue != null) 15 { 16 if (_debug) 17 Trace.WriteLine("Redis Cache Get from local:{0},Result:{1}".FormatWith(key, localValue.ToJson())); 18 19 return TypeConvert.ChangeType<T>(localValue); 20 } 21 } 22 23 Open(); 24 var value = _connection.Strings.GetString(_dbNumber, key); 25 var jsonValue = _connection.Wait(value); 26 Trace.WriteLine("get:{0},result:{1}".FormatWith(key, jsonValue)); 27 if (string.IsNullOrWhiteSpace(jsonValue)) 28 { 29 RemoveKeyDependency(key); 30 return default(T); 31 } 32 33 var cachedValue = jsonValue.FromJson<T>(); 34 35 if (_useLocal) 36 { 37 //设置本地缓存 38 _localCache.Set(key, cachedValue, DateTime.Now + TimeSpan.FromSeconds(_defaultLocalExpiredTime)); 39 } 40 41 return cachedValue; 42 }
Set方法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /// <summary> 2 /// 添加一个对象到到缓存 3 /// </summary> 4 /// <param name="key">键值</param> 5 /// <param name="data">存储到缓存中的对象</param> 6 /// <param name="slidingTime">缓存时间</param> 7 /// <param name="dependencyKeys"> </param> 8 public void Set(string key, object data, TimeSpan slidingTime, params string[] dependencyKeys) 9 { 10 key = GetKey(key); 11 if (data == null) 12 { 13 Remove(key); 14 return; 15 } 16 17 if (_useLocal) 18 { 19 //设置本地缓存 20 _localCache.Set(key, data,DateTime.Now + TimeSpan.FromSeconds(_defaultLocalExpiredTime)); 21 } 22 23 Open(); 24 _connection.Strings.Set(_dbNumber, key, data.ToJson(), (long)slidingTime.TotalSeconds); 25 Trace.WriteLine("Set:{0},value:{1}".FormatWith(key, data.ToJson())); 26 if (dependencyKeys != null && dependencyKeys.Length > 0) 27 AddKeyDependency(key, dependencyKeys); 28 }