淺談Redis中LFU算法源碼解析
Redis 的 LFU(Least Frequently Used,最不經(jīng)常使用)淘汰算法主要用于 maxmemory-policy
設(shè)置為 allkeys-lfu
或 volatile-lfu
時,以最少使用頻率的鍵進行淘汰。其核心實現(xiàn)涉及到 訪問頻率計數(shù) 和 時間衰減機制,源碼主要集中在 src/server.c
和 src/evict.c
文件中。
1. LFU 計數(shù)存儲
Redis 采用 8-bit 的 LRU
字段 來存儲訪問頻率計數(shù),存儲在 robj
結(jié)構(gòu)體的 lru
字段中:
struct redisObject { unsigned type:4; unsigned encoding:4; unsigned lru:LRU_BITS; // 用于 LRU/LFU 計算 int refcount; void *ptr; };
其中 lru
變量的 8-bit 空間被拆分:
- 前 6-bit(
counter
):用于存儲訪問計數(shù),最大值 63。 - 后 2-bit(
clock
):用于時間衰減計算。
2. 訪問計數(shù)的計算
LFU 計數(shù)在每次訪問鍵時都會遞增,但遞增方式不是簡單 +1
,而是使用 對數(shù)增長 方式,避免某些鍵因高訪問量而壟斷:
unsigned long LFUDecrAndReturn(robj *o) { unsigned long counter = LFUGetCounter(o); if (counter == 0) return 0; if (rand() % (counter + 1) == 0) counter--; LFUSetCounter(o, counter); return counter; }
計數(shù)增長時:
int LFUIncrAndReturn(robj *o) { unsigned long counter = LFUGetCounter(o); if (counter < 63) { if (rand() % (counter + 1) == 0) counter++; } LFUSetCounter(o, counter); return counter; }
這意味著:
- 初始時計數(shù)增長較快 (
1 → 2 → 3
…) - 計數(shù)越高,增長概率越低(符合 對數(shù)曲線)
- 這樣可以防止某些高訪問量鍵長期存活。
3. LFU 訪問頻率的衰減
由于有些數(shù)據(jù)可能短期內(nèi)訪問頻繁,但長期不再被訪問,因此 Redis 采用了 時間衰減機制:
每 1 分鐘 遞減一次訪問計數(shù)。
使用 2-bit 記錄最近訪問的時間 lfu_clock
,每隔 60s 觸發(fā) 衰減:
#define LFU_INIT_VAL 5 // 初始訪問計數(shù) unsigned long LFUDecrAndReturn(robj *o) { unsigned long counter = LFUGetCounter(o); if (counter == 0) return 0; if (rand() % (counter + 1) == 0) counter--; LFUSetCounter(o, counter); return counter; }
該方法會按照一定概率減少計數(shù),確保 近期訪問過的鍵不會輕易被淘汰,而 長時間未訪問的鍵會逐步淘汰。
4. 淘汰策略
當(dāng) maxmemory
超出時,Redis 需要淘汰一部分數(shù)據(jù),LFU 主要執(zhí)行:
遍歷數(shù)據(jù),找到訪問計數(shù)最小的鍵。
采用 volatile-lfu
或 allkeys-lfu
進行數(shù)據(jù)刪除:
evictionPoolPopulate(dict *sample_dict) { // 從字典中隨機采樣 N 個鍵 for (i = 0; i < EVPOOL_SIZE; i++) { lfu = LFUGetCounter(entry); if (lfu < min_lfu) { min_lfu = lfu; min_entry = entry; } } // 淘汰訪問次數(shù)最少的 dictDelete(sample_dict, min_entry); }
采用 近似隨機采樣,而不是遍歷所有鍵,提高效率。
5. 關(guān)鍵總結(jié)
- 存儲方式:使用
robj.lru
變量的 8-bit 空間存儲訪問計數(shù)和時間信息。 - 訪問計數(shù)增長:采用對數(shù)增長策略,防止單個鍵因訪問量過大而占用內(nèi)存。
- 時間衰減:每分鐘對訪問頻率計數(shù)進行衰減,確保長期未訪問的鍵被淘汰。
- 淘汰策略:采樣多個鍵,找到訪問計數(shù)最少的鍵進行刪除。
Redis 的 LFU 機制相比 LRU 更適用于熱點數(shù)據(jù)訪問場景,避免了某些短期流行的鍵占用大量緩存,同時也能讓真正的 高頻訪問數(shù)據(jù) 存活更久。
到此這篇關(guān)于淺談Redis中LFU算法源碼解析的文章就介紹到這了,更多相關(guān)Redis LFU算法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Redis中的3種特殊數(shù)據(jù)結(jié)構(gòu)詳解
在本文中,我們對三種特殊的數(shù)據(jù)類型進行了介紹,它們分別是geospatial(地理空間數(shù)據(jù)類型)、HyperLogLogs和Bitmaps(位圖),這些數(shù)據(jù)類型在不同的領(lǐng)域和應(yīng)用中發(fā)揮著重要作用,并且具有各自獨特的特性和用途,對Redis特殊數(shù)據(jù)結(jié)構(gòu)相關(guān)知識感興趣的朋友一起看看吧2024-02-02redis的list數(shù)據(jù)類型相關(guān)命令介紹及使用
本文主要介紹了redis的list數(shù)據(jù)類型相關(guān)命令介紹及使用,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-01-01