服务器本地缓存
1.简介
服务器本地缓存是使用一个静态字典,根据唯一准确描述缓存的key来将值存入内存中
新建一个CustomCache类,用来保存缓存
代码如下:
using System; using System.Collections.Generic; namespace ceshi { class CustomCache { private static Dictionary<string, object> CustomCacheDictionary = new Dictionary<string, object>(); public static void Add(string key,object value)//添加缓存 { CustomCacheDictionary.Add(key,value); } public static T GetT<T>(string key)//获取缓存的值 { return (T)CustomCacheDictionary[key]; } public static bool Exsits(string key)//判断缓存是否存在 { return CustomCacheDictionary.ContainsKey(key); } } }
Program:
using System; using System.Threading.Tasks; using System.Threading; namespace ceshi { class Program { static void Main(string[] args) { for (int i = 0; i < 5; i++) { string key = "add_key_101";//唯一准确描述缓存的key int result; if (CustomCache.Exsits(key)) { result = CustomCache.GetT<int>(key); } else {
//----------------调用模拟函数--------------- result = Simulation(); //----------------添加缓存------------------- CustomCache.Add(key, result); } Console.WriteLine(result); } Console.Read(); } static int Simulation() { //-------------模拟获取数据的时耗-------------- int result; Thread.Sleep(2000); Random random = new Random(); result = random.Next(0, 100); return result; } } }
2.封装调用函数
现实羡慕中缓存是无处不在的,所以为了方便使用缓存,可以将上面红色字体的代码封装到CustomCache里面,以方便调用
CustomCache类添加一个函数:
using System; using System.Collections.Generic; namespace ceshi { class CustomCache { private static Dictionary<string, object> CustomCacheDictionary = new Dictionary<string, object>(); public static void Add(string key,object value) { CustomCacheDictionary.Add(key,value); } public static T GetT<T>(string key) { return (T)CustomCacheDictionary[key]; } public static bool Exsits(string key) { return CustomCacheDictionary.ContainsKey(key); } public static T GetDataT<T>(string key,Func<T> func)//委托Func是无参有返回值 { T result = default(T); if(Exsits(key)) { result=(T)CustomCacheDictionary[key]; } else { result=func.Invoke();//实例化外部模拟获取数据的函数,也就是运行这个函数并接收返回值 Add(key, result); } return result; } } }
Program:
using System; using System.Threading.Tasks; using System.Threading; namespace ceshi { class Program { static void Main(string[] args) { for (int i = 0; i < 5; i++) { string key = "add_key_101";//唯一准确描述缓存的key int result; result=CustomCache.GetDataT<int>(key, () => Simulation()); Console.WriteLine(result); } Console.Read(); } static int Simulation() { //-------------模拟获取数据的时耗-------------- int result; Thread.Sleep(2000); Random random = new Random(); result = random.Next(0, 100); return result; } } }
这样在多次调用时可以节省很多代码量。
3.缓存进阶
在缓存中,如果数据库的数据被修改了,缓存就可能产生脏数据
所以应该使用一些方法,来及时清除更新这些脏数据
1)更新的数据,影响一条缓存
每次主动修改数据库时,根据key删除对应的缓存(为什么是删除不是更新呢,缓存一般不更新,删除之后再次访问会继续做缓存)
CustomCacheDictionary.Remove(key);
2)更新的数据,影响到多条缓存
可以直接删除所有缓存,简单粗暴
CustomCacheDictionary.Clear();
3)跟新的数据,影响多条缓存,但这些缓存的key有大致相同的结构
如,均以_customcategory为结尾
这样我们可以根据条件删除缓存
using System; using System.Collections.Generic; namespace ceshi { class CustomCache { private static Dictionary<string, object> CustomCacheDictionary = new Dictionary<string, object>(); public static void Add(string key,object value) { CustomCacheDictionary.Add(key,value); } public static T GetT<T>(string key) { return (T)CustomCacheDictionary[key]; } public static bool Exsits(string key) { return CustomCacheDictionary.ContainsKey(key); } public static T GetDataT<T>(string key,Func<T> func)//Func是无参有返回值 { T result = default(T); if(Exsits(key)) { result=(T)CustomCacheDictionary[key]; } else { result=func.Invoke(); Add(key, result); } return result; } public static void RemoveCondition(Func<string,bool> func)//根据条件删除缓存,委托为给一个string类型的参数,返回bool { List<string> keyList = new List<string>(); foreach(string item in CustomCacheDictionary.Keys) { if(func.Invoke(item)) { keyList.Add(item); } } keyList.ForEach(key => CustomCacheDictionary.Remove(key)); } } }
Program
using System; using System.Threading.Tasks; using System.Threading; namespace ceshi { class Program { static void Main(string[] args) { for (int i = 0; i < 4; i++)//添加四条以_customcategory为结尾的缓存 { string key = $"add_key_10{i}_customcategory";//唯一准确描述缓存的key int result; result = CustomCache.GetDataT<int>(key, () => Simulation()); Console.WriteLine(result); } { CustomCache.RemoveCondition((s) => s.EndsWith("_customcategory"));清除以_customcategory为结尾的缓存 } Console.Read(); } static int Simulation() { //-------------模拟获取数据的时耗-------------- int result; Thread.Sleep(2000); Random random = new Random(); result = random.Next(0, 100); return result; } } }
4)缓存过期
在缓存中,我们基本需要加上过期时间,这样可以保证数据的可靠性
using System; using System.Collections.Generic; namespace ceshi { class CustomCache { private static Dictionary<string, KeyValuePair<object, DateTime>> CustomCacheDictionary = new Dictionary<string, KeyValuePair<object, DateTime>>(); public static void Add(string key, object value, int second = 3)//second定义数据过期时间,单位是秒 { CustomCacheDictionary.Add(key, new KeyValuePair<object, DateTime>(value, DateTime.Now.AddSeconds(second))); } public static T GetT<T>(string key) { return (T)CustomCacheDictionary[key].Key; } public static bool IsExsits(string key)//判断缓存是否存在和是否过期 { if (CustomCacheDictionary.ContainsKey(key)) { if (CustomCacheDictionary[key].Value > DateTime.Now) { return true; } else//过期,删除 { CustomCacheDictionary.Remove(key); return false; } } else { return false; } } public static T GetDataT<T>(string key, Func<T> func)//Func是无参有返回值 { T result = default(T); if (IsExsits(key)) { result = (T)CustomCacheDictionary[key].Key; } else { result = func.Invoke(); Add(key, result); } return result; } } }
Program:
using System; using System.Threading.Tasks; using System.Threading; namespace ceshi { class Program { static void Main(string[] args) { for (int i = 0; i < 500; i++) { string key = $"add_key_10_customcategory";//唯一准确描述缓存的key int result; result = CustomCache.GetDataT<int>(key, () => Simulation()); Console.WriteLine(result); Thread.Sleep(1000); } Console.Read(); } static int Simulation() { //-------------模拟获取数据的时耗-------------- int result; Thread.Sleep(2000); Random random = new Random(); result = random.Next(0, 100); return result; } } }
结果:
我们定义缓存的过期时间是3秒后,当然现实中需要按要求合理定义过期时间,这里只是为了方便测试。
以上这个方法还有一个缺陷,那就是如果一个缓存数据一直没有被访问到,那过期了也不会被判断并删除,会占用内存。
所以,我们需要新开一个线程,让他实时进行过期检索操作。
在类中添加一个构造函数,这样第一次使用这个类时,构造函数也会被调用,即可以马上打开这个线程进行监听
static CustomCache() { Task.Run(() => { while(true) { List<string> keyList = new List<string>(); foreach(string item in CustomCacheDictionary.Keys) { if (CustomCacheDictionary[item].Value > DateTime.Now) { //还没过期,无需操作 } else//过期,删除 { keyList.Add(item); } } keyList.ForEach(key => CustomCacheDictionary.Remove(key)); Thread.Sleep(60*1000); } }); }
4.缓存的使用场景
适合使用缓存的资源一般具有以下特性
//常用 //耗时 //稳定 //即时性要求低 //内存小
举几个小例子
可用于
//session用户信息 //权限数据 //菜单 //商品列表 //基础资料 系统设置
不可用于
//股票数据 //图片/视频