引言
Redis 緩存使用內存來保存數據,隨著需要緩存的數據量越來越大,有限的緩存空間不可避免地會被寫滿。此時,應該怎么辦?本篇文章接下來就來聊聊緩存滿了之后的數據淘汰機制。
值得注意的是,在 Redis 中?過期策略 和 內存淘汰策略 是兩個完全不同的概念。Redis 過期策略指的是 Redis 使用哪種策略,來刪除已經過期的鍵值對;而內存淘汰機制指的是當 Redis 運行內存已經超過設置的最大內存之后,將采用什么策略來刪除符合條件的鍵值對,以此來保障 Redis 高效的運行。
Redis 最大運行內存
只有在 Redis 的運行內存達到了某個閥值,才會觸發內存淘汰機制,這個閥值就是我們設置的最大運行內存,此值在 Redis 的配置文件中可以找到,配置項為?maxmemory。
內存淘汰執行流程,如下圖所示:
查詢最大運行內存
我們可以使用命令?config get maxmemory?來查看設置的最大運行內存,命令如下:
127.0.0.1:6379>?config?get?maxmemory 1)?"maxmemory" 2)?"0"
我們發現此值竟然是 0,這是 64 位操作系統默認的值,當 maxmemory 為 0 時,表示沒有內存大小限制。
注意:32 位操作系統,默認的最大內存值是 3GB。
內存淘汰策略
查看 Redis 內存淘汰策略
我們可以使用?config get maxmemory-policy?命令,來查看當前 Redis 的內存淘汰策略,命令如下:
127.0.0.1:6379>?config?get?maxmemory-policy 1)?"maxmemory-policy" 2)?"noeviction"
可以看出此 Redis 使用的是 noeviction 類型的內存淘汰機制,它表示當運行內存超過最大設置內存時,不淘汰任何數據,但新增操作會報錯。
內存淘汰策略分類
早期版本的 Redis 有以下 6 種淘汰策略:
noeviction:不淘汰任何數據,當內存不足時,新增操作會報錯,Redis 默認內存淘汰策略;
allkeys-lru:淘汰整個鍵值中最久未使用的鍵值;
allkeys-random:隨機淘汰任意鍵值;
volatile-lru:淘汰所有設置了過期時間的鍵值中最久未使用的鍵值;
volatile-random:隨機淘汰設置了過期時間的任意鍵值;
volatile-ttl:優先淘汰更早過期的鍵值。
在 Redis 4.0 版本中又新增了 2 種淘汰策略:
volatile-lfu:淘汰所有設置了過期時間的鍵值中,最少使用的鍵值;
allkeys-lfu:淘汰整個鍵值中最少使用的鍵值。
其中?allkeys-xxx?表示從所有的鍵值中淘汰數據,而?volatile-xxx?表示從設置了過期鍵的鍵值中淘汰數據。
修改 Redis 內存淘汰策略
設置內存淘汰策略有兩種方法,這兩種方法各有利弊,需要使用者自己去權衡。
方式一:通過“config set maxmemory-policy 策略”命令設置。它的優點是設置之后立即生效,不需要重啟 Redis 服務,缺點是重啟 Redis 之后,設置就會失效。
方式二:通過修改 Redis 配置文件修改,設置“maxmemory-policy 策略”,它的優點是重啟 Redis 服務后配置不會丟失,缺點是必須重啟 Redis 服務,設置才能生效。
內存淘汰算法
從內存淘汰策略分類上,我們可以得知,除了隨機刪除和不刪除之外,主要有兩種淘汰算法:LRU 算法和?LFU 算法。
LRU 算法
LRU 全稱是 Least Recently Used 譯為最近最少使用,是一種常用的頁面置換算法,選擇最近最久未使用的頁面予以淘汰。
1. LRU 算法實現
LRU 算法需要基于鏈表結構,鏈表中的元素按照操作順序從前往后排列,最新操作的鍵會被移動到表頭,當需要內存淘汰時,只需要刪除鏈表尾部的元素即可。
2. 近 LRU 算法
Redis 使用的是一種近似 LRU 算法,目的是為了更好的節約內存,它的實現方式是給現有的數據結構添加一個額外的字段,用于記錄此鍵值的最后一次訪問時間,Redis 內存淘汰時,會使用隨機采樣的方式來淘汰數據,它是隨機取 5 個值(此值可配置),然后淘汰最久沒有使用的那個。
3. LRU 算法缺點
LRU 算法有一個缺點,比如說很久沒有使用的一個鍵值,如果最近被訪問了一次,那么它就不會被淘汰,即使它是使用次數最少的緩存,那它也不會被淘汰,因此在 Redis 4.0 之后引入了 LFU 算法,下面我們一起來看。
LFU 算法
LFU 全稱是 Least Frequently Used 翻譯為最不常用的,最不常用的算法是根據總訪問次數來淘汰數據的,它的核心思想是“如果數據過去被訪問多次,那么將來被訪問的頻率也更高”。
LFU 解決了偶爾被訪問一次之后,數據就不會被淘汰的問題,相比于 LRU 算法也更合理一些。
在 Redis 中每個對象頭中記錄著 LFU 的信息,源碼如下:
typedef?struct?redisObject?{ ????unsigned?type:4; ????unsigned?encoding:4; ????unsigned?lru:LRU_BITS;?/*?LRU?time?(relative?to?global?lru_clock)?or ????????????????????????????*?LFU?data?(least?significant?8?bits?frequency ????????????????????????????*?and?most?significant?16?bits?access?time).?*/ ????int?refcount; ????void?*ptr; }?robj;
在 Redis 中 LFU 存儲分為兩部分,16 bit 的 ldt(last decrement time)和 8 bit 的 logc(logistic counter)。
logc 是用來存儲訪問頻次,8 bit 能表示的最大整數值為 255,它的值越小表示使用頻率越低,越容易淘汰;
ldt 是用來存儲上一次 logc 的更新時間。
總結
綜上所述我們了解到,Redis 內存淘汰策略和過期回收策略是完全不同的概念,內存淘汰策略是解決 Redis 運行內存過大的問題的,通過與?maxmemory?比較,決定要不要淘汰數據,根據?maxmemory-policy?參數,決定使用何種淘汰策略,在 Redis 4.0 之后已經有?8 種淘汰策略了,默認的策略是?noeviction?當內存超出時不淘汰任何鍵值,只是新增操作會報錯。
編輯:黃飛
?
評論
查看更多