帶你走進快取世界(4) 快取之緩

2022-02-20 13:46:32 字數 3343 閱讀 9762

快取二字,從字面上分為兩塊:「緩」與「存」。上節我們提到的快取原理,其實是在講的乙個「存」字,如何訪問。大致回顧下是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方面做過度設計會讓人深陷其中 快取的資料越多,系統的維護成本就越高,所以找準需要快取的點尤為重要...

走進快取的世界(一) 開篇

系列文章 對於程式設計師來說多多少少都懂一點演算法,演算法是什麼?演算法是 時間 與 空間 的互換策略。我們常常研究乙個演算法的時間複雜度和空間複雜度,如果我們有絕對足夠的時間和空間,那麼演算法就不需要了,可惜這種條件是不存在的,只是在某些情況下我們會協調兩者從而達到效能上的平衡。快取是一種 用空間...

走進快取的世界(一) 開篇

對於程式設計師來說多多少少都懂一點演算法,演算法是什麼?演算法是 時間 與 空間 的互換策略。我們常常研究乙個演算法的時間複雜度和空間複雜度,如果我們有絕對足夠的時間和空間,那麼演算法就不需要了,可惜這種條件是不存在的,只是在某些情況下我們會協調兩者從而達到效能上的平衡。快取是一種 用空間換時間 的...