本文主要講的是目前存在的幾種緩存算法, 沒錯, 我又來誤人子弟了.
內容會圍繞近幾年比較流行的LFU, LRU, 還有W-TinyLRU這麼三種緩存算法來講, 儘量使用最簡練的文本.
LFU
近期最少使用算法,即LFU算法(Least Frequently Used algorithm)。 這種算法會淘汰近期最少訪問的緩存, 仔細分析一下, 沒錯,這是一種非常合理的算法,因為到目前為止最少使用的頁面, 很可能也是將來最少訪問的頁面。 該算法既充分利用了內存中緩存調度情況的歷史信息,又正確反映了程序的局部性。
但是,這種算法的開銷極其高, 為了記錄每個緩存的使用情況, 你不得不為每一個緩存增加一個很大的計數器, 每次到達臨界點, 我們還需要找到所有計數器中最少的緩存, 淘汰它.
核心思想:如果一個數據在最近一段時間內使用次數很少,那麼在將來一段時間內被使用的可能性也很小.
通常我們會這麼去實現:
外部結構為Array, 存儲元素為KV. K: 該緩存訪問次數. V: 緩存本身.
數組按照K排序. 每次緩存大小即將臨界, 淘汰K最小的緩存.
順便一提 Window-LFU 它是LFU算法的改良版. LFU中緩存的訪問次數記錄的時間範圍為整個程序的生命週期,
在Window-LFU中只對特定範圍的訪問次數進行淘汰. (比如最近10次訪問的緩存.)
LRU
最久沒有使用算法,即LRU算法(Least Recently Used algorithm)。 這種算法把近期最久沒有被訪問過的頁面作為被替換的頁面。 它把LFU算法中要記錄數量上的"多"與"少"簡化成判斷"有"與"無",因此,實現起來比較容易。
核心思想:如果在一段時間內長時間不訪問的頁面將來也不會訪問.
實現很簡單:
假設我們的緩存容量大小為2, 最多緩存2個元素. 下面是緩存的訪問順序.
1 2 2 3
第一次: [1]
第二次: [2, 1]
第三次: [2, 1]
第四次: [3, 2]
最新訪問的緩存會放在首位, 很久沒有訪問的緩存自然而然的就到了末尾, 然後被淘汰.
W-TinyLRU
知名緩存框架 caffeine 就是使用的這種算法 這種算法結合LRU和LFU算法,解決了突發流量問題帶來的。
整個算法數據結構分成三個段,分別為 Eden,Probation,Protected 三個隊列
Eden隊列: 在caffeine中規定只能為緩存容量的%1,如果size=100, 那這個隊列的有效大小就等於1。這個隊列中記錄的是新到的數據, 防止突發流量由於之前沒有訪問頻率,而導致被淘汰。 比如有一部新劇上線,在最開始其實是沒有訪問頻率的, 防止上線之後被其他緩存淘汰出去,而加入這個區域。
Probation隊列:叫做緩刑隊列,在這個隊列就代表你的數據相對比較冷,馬上就要被淘汰了。
這個有效大小為size減去eden減去protected。Protected隊列:在這個隊列中,可以稍微放心一下了,你暫時不會被淘汰, 但是別急,如果Probation隊列沒有數據了或者Protected數據滿了, 你也將會被面臨淘汰的尷尬局面。當然想要變成這個隊列, 需要把Probation訪問一次之後,就會提升為Protected隊列。 這個有效大小為(size減去eden) X 80% 如果size =100,就會是79。
區域規則:
- 所有的新數據都會進入Eden。
- Eden滿了,淘汰進入Probation。
- 如果在Probation中訪問了其中某個數據,則這個數據升級為Protected。
- 如果Protected滿了又會繼續降級為Probation。
Probation的淘汰算法也比較有意思, 取出隊尾和隊首的元素, 然後讓這兩皇城PK, 輸了就淘汰了.
皇城PK規則:
1.如果隊尾元素的頻度大於隊首,那麼就直接淘汰隊首,
2.當隊尾頻度小於等於隊首,且頻度小於5的時候,直接將其淘汰
3.當隊尾頻度小於等於隊首,且頻度大於5的時候,通過隨機的方式進行淘汰任意一個。(看誰運氣好咯)