.Net 数据缓存浅析
目录
1场景出发
1.1数据请求
1.2优化改进
2缓存
3缓存进阶
3.1缓存清除
3.2有效性
3.3线程安全
4适用场景和优劣
4.1适用场景
4.2优劣
5结语
1场景出发
1.1数据请求
小吴开发了一个购物网站,其中涉及到这样一个环节:访客用户请求页面时,会请求数据库获取商品分类信息,然后返回该数据,展示商品的分类
对于这个环节,他是这样处理的
1 /// <summary> 2 /// 模拟数据库获取数据耗时操作 3 /// </summary> 4 public class DataSource 5 { 6 /// <summary> 7 /// 获取商品分类 8 /// </summary> 9 /// <returns></returns> 10 public static string GetCategories() 11 { 12 Thread.Sleep(5000);//模拟耗时 13 return "There are categories"; 14 } 15 }
1 class Program 2 { 3 4 static void Main(string[] args) 5 { 6 string cats = DataSource.GetCategories(); 7 8 Console.WriteLine(cats); 9 10 Console.ReadKey(); 11 } 12 }
在初阶阶段,访客用户数量比较少,网站能够很好的运营
1.2优化改进
随着访客用户数量的增加,服务器压力越来越大,不少用户开始向客服抱怨,网页响应速度太慢
小吴不得不开始优化系统,他考虑到商品分类信息请求次数很多,但每次却是请求相同的数据,于是他决定把这部分数据放在常驻内存的静态变量中,然后在数据库直接获取数据上再加一层,来提供数据服务,改进如下:
1 /// <summary> 2 /// 模拟数据库获取数据耗时操作 3 /// </summary> 4 public class DataSource 5 { 6 /// <summary> 7 /// 获取商品分类 8 /// </summary> 9 /// <returns></returns> 10 public static string GetCategories() 11 { 12 Thread.Sleep(5000);//模拟耗时 13 return "There are categories"; 14 } 15 }
1 /// <summary> 2 /// 静态字典缓存数据 3 /// </summary> 4 public class MyCache 5 { 6 private static Dictionary<string, object> _dictionary = new Dictionary<string, object>(); 7 8 public static void Add(string key,object obj) 9 { 10 _dictionary.Add(key, obj); 11 } 12 13 public static bool Exist(string key) 14 { 15 return _dictionary.ContainsKey(key); 16 } 17 18 public static void remove(string key) 19 { 20 _dictionary.Remove(key); 21 } 22 23 public static void removeAll(string key) 24 { 25 _dictionary = new Dictionary<string, object>(); 26 } 27 28 public static T Get<T>(string key) 29 { 30 return (T)_dictionary[key]; 31 } 32 }
1 /// <summary> 2 /// 数据处理类 3 /// </summary> 4 public class DataProxy 5 { 6 /// <summary> 7 /// 获取商品分类 8 /// </summary> 9 /// <returns></returns> 10 public static string GetCategories() 11 { 12 if (MyCache.Exist("Categories")) 13 { 14 return MyCache.Get<string>("Categories"); 15 } 16 17 else 18 { 19 MyCache.Add("Categories", DataSource.GetCategories()); 20 21 return DataSource.GetCategories(); 22 } 23 } 24 }
1 class Program 2 { 3 4 static void Main(string[] args) 5 { 6 string cats,cats1, cats2; 7 8 cats = DataProxy.GetCategories(); 9 10 Console.WriteLine(cats); 11 12 cats1 = DataProxy.GetCategories(); 13 14 Console.WriteLine(cats1); 15 16 cats2 = DataProxy.GetCategories(); 17 18 Console.WriteLine(cats2); 19 20 Console.ReadKey(); 21 } 22 }
经过这样的优化后,网站响应速度一下子就得到了提升
2缓存
上述场景就是对缓存的一种应用,它是系统性能优化的第一步
原因是使用缓存,缩短了获取数据的时间
这里重点讲解的是服务器端数据缓存,它只是缓存的一部分
下面是http请求过程
可以看到几乎各个环节都涉及到了缓存
3缓存进阶
上述只是数据缓存最简单的例子,在实际的开发过程中,我们还会面对更多的情形,下面我来简单的进阶一下
3.1缓存清理
当我们已知一条缓存的数据已经更新,我们该如何做呢?
上述代码已经解决了这个问题,Remove方法:即去掉这个键值对,然后在数据库获取最新的数据
那么如果多条缓存被影响,我们该如何做呢?
上述的RemoveAll方法?当然这是最直接有效的方法,但是这样会造成缓存穿透,在一瞬间丢失所有的缓存,数据库的压力将会骤增
一个比较有效的方法:我们在添加缓存的时候,把缓存的key值与它的数据义务相联系,即命名更加有意义,这样当某部分受到影响的时候,我们可以直接根据命名来确定是否更改
3.2缓存有效性
在使用缓存的过程中,我们遇到更多的情况是:程序根本不知道,数据已经发生变化
面对这种情况,我们一般采取的措施:给缓存加上有效期
在有效期内,直接使用缓存,超过有效期,在数据库获取最新数据并缓存起来
我们也可以同时增加一条线程,来不间断的主动检查缓存有效期,避免只有在使用的时候才检查的被动状态
代码如下:
1 /// <summary> 2 /// 静态字典缓存数据 3 /// </summary> 4 public class MyCache 5 { 6 /// <summary> 7 /// 每1个小时主动检查缓存过期项一次 8 /// </summary> 9 static MyCache() 10 { 11 Task.Run(() => 12 { 13 Thread.Sleep(3600 * 1000); 14 15 List<string> keyList = new List<string>(); //过期key集合 16 17 foreach (var item in _dictionary.Keys) 18 { 19 if (_dictionary[item].Value < DateTime.Now) 20 { 21 keyList.Add(item); //已过期 22 } 23 } 24 25 keyList.ForEach(p => { _dictionary.Remove(p); }); 26 }); 27 } 28 29 private static Dictionary<string, KeyValuePair<object, DateTime>> _dictionary 30 = new Dictionary<string, KeyValuePair<object, DateTime>>(); 31 32 public static void Add(string key, object obj, int minute = 60) 33 { 34 _dictionary.Add(key, new KeyValuePair<object, DateTime>(obj, DateTime.Now.AddMinutes(minute))); 35 } 36 37 public static bool Exist(string key) 38 { 39 return _dictionary.ContainsKey(key); 40 } 41 42 public static void remove(string key) 43 { 44 _dictionary.Remove(key); 45 } 46 47 public static void removeAll(string key) 48 { 49 _dictionary = new Dictionary<string, KeyValuePair<object, DateTime>>(); 50 } 51 52 public static T Get<T>(string key) 53 { 54 return (T)_dictionary[key].Key; 55 } 56 }
3.3线程安全
在单线程下,上述缓存简例,是没有问题的,可是我们往往面对的是多线程迸发,那么上述的例子,就会有线程安全问题
在键值对添加这里,如果多个线程访问,就会因为添加相同的键值,而导致程序崩溃
所以我们可以加锁,只允许一个线程先访问,后续的线程进来时判断键值是否已经存在
代码如下
1 /// <summary> 2 /// 静态字典缓存数据 3 /// </summary> 4 public class MyCache 5 { 6 private static object _lock = new object(); 7 /// <summary> 8 /// 每1个小时主动检查缓存过期项一次 9 /// </summary> 10 static MyCache() 11 { 12 Task.Run(() => 13 { 14 Thread.Sleep(3600 * 1000); 15 16 List<string> keyList = new List<string>(); //过期key集合 17 18 foreach (var item in _dictionary.Keys) 19 { 20 if (_dictionary[item].Value < DateTime.Now) 21 { 22 keyList.Add(item); //已过期 23 } 24 } 25 26 keyList.ForEach(p => { _dictionary.Remove(p); }); 27 }); 28 } 29 30 private static Dictionary<string, KeyValuePair<object, DateTime>> _dictionary 31 = new Dictionary<string, KeyValuePair<object, DateTime>>(); 32 33 public static void Add(string key, object obj, int minute = 60) 34 { 35 lock (_lock) 36 { 37 if (_dictionary.ContainsKey(key)) 38 { 39 return; 40 } 41 else 42 { 43 _dictionary.Add(key, new KeyValuePair<object, DateTime>(obj, DateTime.Now.AddMinutes(minute))); 44 } 45 } 46 } 47 48 public static bool Exist(string key) 49 { 50 return _dictionary.ContainsKey(key); 51 } 52 53 public static void remove(string key) 54 { 55 _dictionary.Remove(key); 56 } 57 58 public static void removeAll(string key) 59 { 60 _dictionary = new Dictionary<string, KeyValuePair<object, DateTime>>(); 61 } 62 63 public static T Get<T>(string key) 64 { 65 return (T)_dictionary[key].Key; 66 } 67 }
4适用场景和优劣
没有任何技术是完美的,我们所做的只能是具体问题具体分析
缓存会在够解决一系列问题的同时,带来新的问题,我们所能够做的是扬长避短
4.1适用场景
1数据实时性要求不太高:缓存的本质决定了其一定会有脏数据,即使加了有效期,也会有延迟
2多次请求:如果没有请求多次,那么也没有缓存的必要
2体积小:因为缓存是在程序进程内存里面的,所以空间有限
4.2优劣
优势:能够缩短查询路径,更快的得到响应, 优化系统性能,
劣势:无法确定数据是否是最新的,有所延迟
5结语
至今为止,关于缓存与其他各种技术的相结合和各种关于缓存的框架层出不穷,如果一开始就注其表面,会感到晦涩难以深入,不如透过现象回到本质,以最单纯的想法去理解它,那么反而更容易接近它,只要能够明白这些,那么对于那些立其之上的衍生物,只会是游刃有余
出自:博客园-半路独行
原文地址:https://www.cnblogs.com/banluduxing/p/9238838.html
本文出自于http://www.cnblogs.com/banluduxing 转载请注明出处。