快取二字,從字面上分為兩塊:「緩」與「存」。上節我們提到的快取原理,其實是在講的乙個「存」字,如何訪問。大致回顧下是key對應的hashcode,根據hashcode作為陣列下標來訪問,因為存在hash衝突,速度雖達不到o(1),但也是非常之快。今天就說下「緩」的策略。
緩,便意味著「暫時」的意思,過一段時間就不再存在或被替換掉了,所以我們要說的其實是快取的過期策略。在快取入門篇中,主要提到了cache類的insert的方法,其中的幾個變化的引數寓意著各種快取策略,有具體依賴的有按時間的,一一來看。
按過期時間快取
/// /// 按時間快取類按間隔時間快取///
public class cachebydatetime
/// /// 過期時間
///
public datetime datetime
}/// /// 快取資料詞典
///
private readonly dictionary_dict;
//為了執行緒安全,需要對dict的操作加鎖
private static readonly object lockdict = new object();
public cachebydatetime()
/// /// 新增乙個快取
///
///
///
/// 過期時間
public void add(tkey key, tvalue value, datetime datetime)
else);}
}}
/// /// 獲取快取
///
public tvalue get(tkey key)
else
}return default(tvalue);
}/// /// 移除快取
///
public void remove(tkey key)}}
}
/// /// 新增乙個快取依賴項快取///
///
///
/// 間隔時間
public void add(tkey key, tvalue value, timespan timespan)
else);}
}}
依賴快取相對以上兩個來說是非常複雜的處理過程,比如檔案依賴,會有相應的監測程式(filemonitor)來管理dependency物件。這裡我們便不講解,了解其用處即可,著實因為太過複雜。有興趣的可以看.net原始碼。
lru(least recently used)快取
從名字便知其意,其主要用於限定容量(比如記憶體大小或快取數量)的快取,需要在快取容器滿了之後踢出過期快取的策略,是使用次數最少或很久沒使用的快取項策略。
實現原理一般使用鍊錶方式把所有快取項連起來,每當有新的快取進入則把快取放入鍊錶前端,如果快取被使用則把他提到鍊錶前端,那麼沒被使用的將慢慢趨於鍊錶後端,所以當容量滿了以後,就優先移除鍊錶末尾的快取項。當然,也有其他更為複雜的過期策略,比如同時使用快取時間。雖然此策略和上面的按時間間隔延長快取有點相像,但這個更側重於快取容器大小的管理,畢竟記憶體是有限的,此策略多用於公共快取服務。下面的類是個簡單的lru實現,只限定的快取的長度並沒有大小限制,如果要做大小限制則需要計算每乙個value的大小。
/// /// lrucache以上提到的是我們常用的幾種快取策略,當然還有其他的策略,我們後面也會提到。今天就先到這吧。///
public class lrucache
public tvalue value
public cacheitem left
public cacheitem right
public cacheitem(tkey key, tvalue value)
}private readonly static object lockdict = new object();
private readonly idictionary_dict;
public int length
public lrucache(int maxlength)
//鍊錶頭部
private cacheitem _first;
//鍊錶末端
private cacheitem _last;
public bool haskey(tkey key)
/// /// 新增乙個快取項
///
public void add(tkey key, tvalue value)
//如果只有乙個快取項,則item是first,first和last變為last
else if (_dict.count == 1)
else
//如果超過的鍊錶長度
if (_dict.count >= length)
//將item放入dict
if (_dict.containskey(key))
_dict[key] = new cacheitem(key, value);
else
_dict.add(key, new cacheitem(key, value));}}
/// /// 獲取乙個快取項
///
public tvalue get(tkey key)
var item = _dict[key];
lock (lockdict)
//如果item左側有快取項,則將左側的快取指向item的右側
if (item.left != null)
else
//如果item右側有快取項,則將右側的快取指向item的左側
if (item.right != null)
else
//斷開item的左側,讓item成為first,讓first成為item的右側項
item.left = null;
item.right = _first;
_first.left = item;
_first = item;
}return item.value;
}public void remove(tkey key)
var item = _dict[key];
lock (lockdict)
else
//如果item的右側有值,則將item的右側的左值指向item的左側
if (item.right != null)
else
_dict.remove(key);}}
}
走進快取的世界(二) 快取設計
主要考慮三個問題 系統優化時有一句話必須切記 優化無止境 所以如果快取不是必須的,請果斷去掉,要知道越是業務上覆雜的系統,對cache的使用反而越簡單,因為對於乙個複雜 多變 歷史悠久的系統,在cache方面做過度設計會讓人深陷其中 快取的資料越多,系統的維護成本就越高,所以找準需要快取的點尤為重要...
走進快取的世界(一) 開篇
系列文章 對於程式設計師來說多多少少都懂一點演算法,演算法是什麼?演算法是 時間 與 空間 的互換策略。我們常常研究乙個演算法的時間複雜度和空間複雜度,如果我們有絕對足夠的時間和空間,那麼演算法就不需要了,可惜這種條件是不存在的,只是在某些情況下我們會協調兩者從而達到效能上的平衡。快取是一種 用空間...
走進快取的世界(一) 開篇
對於程式設計師來說多多少少都懂一點演算法,演算法是什麼?演算法是 時間 與 空間 的互換策略。我們常常研究乙個演算法的時間複雜度和空間複雜度,如果我們有絕對足夠的時間和空間,那麼演算法就不需要了,可惜這種條件是不存在的,只是在某些情況下我們會協調兩者從而達到效能上的平衡。快取是一種 用空間換時間 的...