好久前參加過今日頭條的面試,遇到一個題,目前半部分是如何實現 LRU,後半部分是 Redis 中如何實現 LRU。html
個人第一反應是操做系統課程裏學過,應該是內存不夠的場景下,淘汰舊內容的策略。LRU ... Least Recent Used,淘汰掉最不常常使用的。能夠稍微多補充兩句,由於計算機體系結構中,最大的最可靠的存儲是硬盤,它容量很大,而且內容能夠固化,可是訪問速度很慢,因此須要把使用的內容載入內存中;內存速度很快,可是容量有限,而且斷電後內容會丟失,而且爲了進一步提高性能,還有CPU內部的 L1 Cache,L2 Cache等概念。由於速度越快的地方,它的單位成本越高,容量越小,新的內容不斷被載入,舊的內容確定要被淘汰,因此就有這樣的使用背景。node
在通常標準的操做系統教材裏,會用下面的方式來演示 LRU 原理,假設內存只能容納3個頁大小,按照 7 0 1 2 0 3 0 4 的次序訪問頁。假設內存按照棧的方式來描述訪問時間,在上面的,是最近訪問的,在下面的是,最遠時間訪問的,LRU就是這樣工做的。面試
可是若是讓咱們本身設計一個基於 LRU 的緩存,這樣設計可能問題不少,這段內存按照訪問時間進行了排序,會有大量的內存拷貝操做,因此性能確定是不能接受的。redis
那麼如何設計一個LRU緩存,使得放入和移除都是 O(1) 的,咱們須要把訪問次序維護起來,可是不能經過內存中的真實排序來反應,有一種方案就是使用雙向鏈表。算法
總體的設計思路是,可使用 HashMap 存儲 key,這樣能夠作到 save 和 get key的時間都是 O(1),而 HashMap 的 Value 指向雙向鏈表實現的 LRU 的 Node 節點,如圖所示。緩存
LRU 存儲是基於雙向鏈表實現的,下面的圖演示了它的原理。其中 h 表明雙向鏈表的表頭,t 表明尾部。首先預先設置 LRU 的容量,若是存儲滿了,能夠經過 O(1) 的時間淘汰掉雙向鏈表的尾部,每次新增和訪問數據,均可以經過 O(1)的效率把新的節點增長到對頭,或者把已經存在的節點移動到隊頭。app
下面展現了,預設大小是 3 的,LRU存儲的在存儲和訪問過程當中的變化。爲了簡化圖複雜度,圖中沒有展現 HashMap部分的變化,僅僅演示了上圖 LRU 雙向鏈表的變化。咱們對這個LRU緩存的操做序列以下:dom
save("key1", 7)post
save("key2", 0)性能
save("key3", 1)
save("key4", 2)
get("key2")
save("key5", 3)
get("key2")
save("key6", 4)
相應的 LRU 雙向鏈表部分變化以下:
總結一下核心操做的步驟:
完整基於 Java 的代碼參考以下
LRU Cache
那麼問題的後半部分,是 Redis 如何實現,這個問題這麼問確定是有坑的,那就是redis確定不是這樣實現的。
若是按照HashMap和雙向鏈表實現,須要額外的存儲存放 next 和 prev 指針,犧牲比較大的存儲空間,顯然是不划算的。因此Redis採用了一個近似的作法,就是隨機取出若干個key,而後按照訪問時間排序後,淘汰掉最不常常使用的,具體分析以下:
爲了支持LRU,Redis 2.8.19中使用了一個全局的LRU時鐘,server.lruclock
,定義以下,
默認的LRU時鐘的分辨率是1秒,能夠經過改變REDIS_LRU_CLOCK_RESOLUTION
宏的值來改變,Redis會在serverCron()
中調用updateLRUClock
按期的更新LRU時鐘,更新的頻率和hz參數有關,默認爲100ms
一次,以下,
server.unixtime
是系統當前的unix時間戳,當 lruclock 的值超出REDIS_LRU_CLOCK_MAX時,會從頭開始計算,因此在計算一個key的最長沒有訪問時間時,可能key自己保存的lru訪問時間會比當前的lrulock還要大,這個時候須要計算額外時間,以下,
Redis支持和LRU相關淘汰策略包括,
volatile-lru
設置了過時時間的key參與近似的lru淘汰策略allkeys-lru
全部的key均參與近似的lru淘汰策略當進行LRU淘汰時,Redis按以下方式進行的,
Redis會基於server.maxmemory_samples
配置選取固定數目的key,而後比較它們的lru訪問時間,而後淘汰最近最久沒有訪問的key,maxmemory_samples的值越大,Redis的近似LRU算法就越接近於嚴格LRU算法,可是相應消耗也變高,對性能有必定影響,樣本值默認爲5。
看來,雖然一個簡單的概念,在工業界的產品中,爲了追求空間的利用率,也會採用權衡的實現方案。
傳送門 https://zhuanlan.zhihu.com/p/34133067
原文:https://blog.csdn.net/hopeztm/article/details/79547052
關於linkedhashmap實現LRU:https://www.cnblogs.com/lzrabbit/p/3734850.html