緩存算法用於決定緩存系統中哪些數據應該被刪去。
LFU(Least Frequently Used):最近最不常用算法,根據數據的歷史訪問頻率來淘汰數據。
核心思想是:最近使用頻率高的數據很大概率將會再次被使用,而最近使用頻率低的數據,很大概率不會再使用。
做法:把使用頻率最小的數據置換出去。這種算法是完全從使用頻率的角度去考慮的。
執行過程理解:
- 在緩存中查找客戶端需要訪問的數據
- 如果緩存命中,則將訪問的數據從隊列中取出,並將數據對應的頻率計數加1,然后將其放到頻率相同的數據隊列的頭部,比如原來是A(10)->B(9)->C(9)->D(8),D被訪問后,它的time變成了9,這時它被提到A和B之間,而不是繼續在C后面
- 如果沒有命中,表示緩存穿透,將需要訪問的數據從磁盤中取出,加入到緩存隊列的尾部,記頻率為1,這里也是加入到同為1的那一級的最前面
- 如果此時緩存滿了,則需要先置換出去一個數據,淘汰隊列尾部頻率最小的數據,然后再在隊列尾部加入新數據。
存在的問題:
某些數據短時間內被重復引用,並且在很長一段時間內不再被訪問。由於它的訪問頻率計數急劇增加,即使它在相當長的一段時間內不會被再次使用,也不會在短時間內被淘汰。這使得其他可能更頻繁使用的塊更容易被清除,此外,剛進入緩存的新項可能很快就會再次被刪除,因為它們的計數器較低,即使之后可能會頻繁使用。
LRU(Least Recently User) 最近最少使用算法,根據數據的歷史訪問記錄來進行淘汰數據
核心思想是:最近使用的數據很大概率將會再次被使用。而最近一段時間都沒有使用的數據,很大概率不會再使用。
做法:把最長時間未被訪問的數據置換出去。這種算法是完全從最近使用的時間角度去考慮的。
執行過程理解:
- 在緩存中查找客戶端需要訪問的數據 如果緩存命中,則將訪問的數據中隊列中取出,重新加入到緩存隊列的頭部。
- 如果沒有命中,表示緩存穿透,將需要訪問的數據從磁盤中取出,加入到緩存隊列的尾部;
- 如果此時緩存滿了,淘汰隊列尾部的數據,然后再在隊列頭部加入新數據。
存在的問題:
緩存污染:如果某個客戶端訪問大量歷史數據時,可能使緩存中的數據被這些歷史數據替換,其他客戶端訪問數據的命中率大大降低。
ARC(Adaptive Replacement Cache): 自適應緩存替換算法,它結合了LRU與LFU,來獲得可用緩存的最佳使用。
核心思想是:當時訪問的數據趨向於訪問最近的內容,會更多地命中LRU list,這樣會增大LRU的空間; 當系統趨向於訪問最頻繁的內容,會更多地命中LFU list,這樣會增加LFU的空間.
執行過程理解:
1. 整個Cache分成兩部分,起始LRU和LFU各占一半,后續會動態適應調整partion的位置(記為p)除此,LRU和LFU各自有一個ghost list(因此,一共4個list)
2. 在緩存中查找客戶端需要訪問的數據, 如果沒有命中,表示緩存穿透,將需要訪問的數據 從磁盤中取出,放到LRU鏈表的頭部。
3. 如果命中,且LFU鏈表中沒有,則將數據放入LFU鏈表的頭部,所有LRU鏈表中的數據都必須至少被訪問兩次才會進入LFU鏈表。如果命中,且LFU鏈表中存在,則將數據重新放到LFU鏈表的頭部。這么做,那些真正被頻繁訪問的頁面將永遠呆在緩存中,不經常訪問的頁面會向鏈表尾部移動,最終被淘汰出去。
4. 如果此時緩存滿了,則從LRU鏈表中淘汰鏈表尾部的數據,將數據的key放入LRU鏈表對應的ghost list。然后再在鏈表頭部加入新數據。如果ghost list中的元素滿了,先按照先進先出的方式來淘汰ghost list中的一個元素,然后再加入新的元素。
這里注意上面的the cache才是實際的LRU和LFU結合的鏈表,因此是刪除了LRU鏈表的尾部元素,尾部元素對應下面的位置索引是1。
5. 如果沒有命中的數據key處於ghost list中,則表示是一次幽靈(phantom)命中,系統知道,這是一個剛剛淘汰的頁面,而不是第一次讀取或者說很久之前讀取的一個頁面。ARC用這個信息來調整它自己,以適應當前的I/O模式(workload)。這個跡象說明我們的LRU緩存太小了。在這種情況下,LRU鏈表的長度將會被增加1,並將命中的數據key從ghost list中移除,放入LRU鏈表的頭部。顯然,LFU鏈表的長度將會被減少1。同樣,如果一次命中發生在LFU ghost 鏈表中,它會將LRU鏈表的長度減一,以此在LFU 鏈表中加一個可用空間。
FIFO(First in First out),先進先出算法,最先進入的數據,最先被淘汰。
核心思想是:最近剛訪問的,將來訪問的可能性比較大 ,如果一個數據最先進入緩存中,則應該最早淘汰掉。
執行過程理解:
- 利用一個雙向鏈表保存數據,
- 當來了新的數據之后便添加到鏈表末尾,
- 如果Cache存滿數據,則把鏈表頭部數據刪除,
- 然后把新的數據添加到鏈表末尾。
- 在訪問數據的時候,如果存在該數據的話,則返回對應的value值;
存在的問題:
這種絕對的公平方式容易導致效率的降低。例如,如果最先加載進來的頁面是經常被訪問的頁面,這樣做很可能造成常被訪問的頁面替換到磁盤上,導致很快就需要再次發生缺頁中斷,從而降低效率。
2Q(Two queues)
做法:有兩個緩存隊列,一個是FIFO隊列,一個是LRU隊列。當數據第一次訪問時,2Q算法將數據緩存在FIFO隊列里面,當數據第二次被訪問時,則將數據從FIFO隊列移到LRU隊列里面,兩個隊列各自按照自己的方法淘汰數據。
執行過程理解:
- 新訪問的數據插入到FIFO隊列;
- 如果數據在FIFO隊列中一直沒有被再次訪問,則最終按照FIFO規則淘汰;
- 如果數據在FIFO隊列中被再次訪問,則將數據移到LRU隊列頭部;
- 如果數據在LRU隊列再次被訪問,則將數據移到LRU隊列頭部;
- LRU隊列淘汰末尾的數據。