在上一篇文章中,分享了一些LRU基本算法及優化策略,本篇繼續該主題,分享在Redis中LRU算法的使用和優化。git
在介紹Redis的LRU使用以前,咱們須要先要了解一下Redis的內存回收策略。github
Redis做爲一個高性能的內存型的KV數據庫,勢必須要一個機制來控制Redis的內存佔用,避免內存溢出。而和內存佔用限制有關的配置即爲maxmemory
這個參數,經過動態調整該參數便可動態地限制Redis的內存佔用。redis
Redis內存回收策略分爲兩個方面:算法
Redis經過兩種手段刪除過時鍵:數據庫
惰性刪除c#
對於有過時時間的鍵,會在鍵上附加一個過時時間的時間戳,當客戶端訪問到該鍵時,會先檢查過時時間戳,若是該鍵值已過時,則返回空,並刪除該鍵。使用惰性刪除能夠避免維護過時時間的鏈表,可是若是一個過時鍵一直沒有被訪問,則有內存泄漏的問題。數組
定時任務刪除緩存
爲了不惰性刪除形成的內存泄漏,Redis會啓動一個定時任務,默認爲10s運行一次,該定時任務會隨機從數據庫中選取一些鍵並刪除其中過時的鍵,若是發現其中超過25%的鍵都已過時,則重複執行一次該定時任務,直至隨機掃描到的一批鍵中,過時鍵值的佔比小於25%。dom
當Redis的內存佔用超過配置值時,就會執行驅逐政策,按照必定的規則驅逐舊數據,Redis能夠配置六種回收策略:post
能夠根據具體應用的數據訪問方式及配合info
命令計算出的緩存命中率來選擇合適的驅逐策略。
咱們能夠看到有兩條驅逐策略是和LRU相關的,並且Redis對LRU算法進行了一些優化。
Redis 使用了一種基於採樣的近似LRU算法來替代普通的LRU算法,從而避免普通的LRU算法帶來的太高的資源消耗,而像Redis這樣的程序,並不須要驅逐精確的那個最久沒有訪問的鍵。
簡述:Redis隨機採樣一小部分鍵值,並選中其中最久沒有訪問的鍵,而後淘汰。
能夠經過maxmemory-samples
參數來配置每次採樣的鍵數量,通常來講該值配置爲5,當該值爲10時就和普通的LRU算法的回收結果很接近,可是也會帶來相應的資源消耗。Redis官網中有下面這麼一個測試結果:
左上角是普通的LRU的回收結果,由於只會插入和訪問一次,因此就是按照先來後到的順序,將舊數據依次的淘汰了。由於Redis3.0增長了一個驅逐池,因此能夠看到3.0的回收效果要好與2.8的版本。並且能夠看到,提升採樣數量也能提升回收效果。
Redis中有一個全局的LRU時鐘lruclock,該時鐘記錄了自服務啓動後的LRU時間,而且該時鐘每100ms更新一次。當客戶端建立或訪問鍵值時,都會根據這個全局的時鐘更新鍵值自身的LRU時鐘。
如此至關於每一個鍵值都有一個訪問時間的記錄。
在插入數據時,會先判斷是否有足夠的容量,若是沒有,則執行回收策略,驅逐舊數據。此處咱們指定爲LRU相關的回收策略。
一個驅逐池中的Entry的結構以下圖所示:
#define REDIS_EVICTION_POOL_SIZE 16
struct evictionPoolEntry {
unsigned long long idle; /* Object idle time. */
sds key; /* Key name. */
};
複製代碼
其中idle即爲空閒時間,key即爲鍵值,空閒時間就是按照上面的LRU時鐘計算的。驅逐池就是以Entry的idle爲順序組成組成的數組,一共有16個Entry。
當須要在驅逐池中插入一個鍵時,首先須要填充驅逐池,按照配置的採樣參數從數據庫中隨機挑選幾個鍵值,而後依次執行下面的操做。
該鍵值會依次比較驅逐池中尋找該鍵空閒時間大的鍵值:
而後就到了真正刪除鍵的邏輯了:
在驅逐池中,依次從空閒時間最長的鍵值開始刪除,直到有足夠的數據空間。
對這部分感興趣的道友,能夠去看下Redis的源碼,主要是freeMemoryIfNeeded()方法
和evictionPoolPopulate()方法
。
本文介紹了Redis的內存回收策略及Redis近似LRU算法的原理和實現。從Redis對LRU算法的優化中,咱們能夠發現一個好的設計,並非說要有多麼準確,有時候能夠在準確性和性能之間作權衡,以知足本身真正想要實現的功能。