目錄
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 轉載請注明出處。