日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

Redis LRU 淘汰原理

發布時間:2024/4/13 数据库 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Redis LRU 淘汰原理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

思考(作業):基于一個數據結構做緩存,怎么實現LRU——最長時間不被訪問的元素在超過容量時刪除?

問題:如果基于傳統LRU 算法實現Redis LRU 會有什么問題?

需要額外的數據結構存儲,消耗內存。

Redis LRU 對傳統的LRU 算法進行了改良,通過隨機采樣來調整算法的精度。

如果淘汰策略是LRU,則根據配置的采樣值maxmemory_samples(默認是5 個),隨機從數據庫中選擇m 個key, 淘汰其中熱度最低的key 對應的緩存數據。所以采樣參數m 配置的數值越大, 就越能精確的查找到待淘汰的緩存數據,但是也消耗更多的CPU 計算,執行效率降低。

問題:如何找出熱度最低的數據?

Redis 中所有對象結構都有一個lru 字段, 且使用了unsigned 的低24 位,這個字段用來記錄對象的熱度。對象被創建時會記錄lru 值。在被訪問的時候也會更新lru 的值。但是不是獲取系統當前的時間戳,而是設置為全局變量server.lruclock 的值。

源碼:server.h

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;

server.lruclock 的值怎么來的?

Redis 中有個定時處理的函數serverCron , 默認每100 毫秒調用函數updateCachedTime 更新一次全局變量的server.lruclock 的值,它記錄的是當前unix時間戳。

源碼:server.c

void updateCachedTime(void) {time_t unixtime = time(NULL);atomicSet(server.unixtime,unixtime);server.mstime = mstime();struct tm tm;localtime_r(&server.unixtime,&tm);server.daylight_active = tm.tm_isdst; }

問題:為什么不獲取精確的時間而是放在全局變量中?不會有延遲的問題嗎?

這樣函數lookupKey 中更新數據的lru 熱度值時,就不用每次調用系統函數time,可以提高執行效率。

OK,當對象里面已經有了LRU 字段的值,就可以評估對象的熱度了。

函數estimateObjectIdleTime 評估指定對象的lru 熱度,思想就是對象的lru 值和全局的server.lruclock 的差值越大(越久沒有得到更新), 該對象熱度越低。

源碼evict.c

/* Given an object returns the min number of milliseconds the object was never * requested, using an approximated LRU algorithm. */ unsigned long long estimateObjectIdleTime(robj *o) {unsigned long long lruclock = LRU_CLOCK();if (lruclock >= o->lru) {return (lruclock - o->lru) * LRU_CLOCK_RESOLUTION;} else {return (lruclock + (LRU_CLOCK_MAX - o->lru)) *LRU_CLOCK_RESOLUTION;} }

server.lruclock 只有24 位,按秒為單位來表示才能存儲194 天。當超過24bit 能表示的最大時間的時候,它會從頭開始計算。

server.h

#define LRU_CLOCK_MAX ((1<<LRU_BITS)-1) /* Max value of obj->lru */

在這種情況下,可能會出現對象的lru 大于server.lruclock 的情況,如果這種情況出現那么就兩個相加而不是相減來求最久的key。

為什么不用常規的哈希表+雙向鏈表的方式實現?需要額外的數據結構,消耗資源。而Redis LRU 算法在sample 為10 的情況下,已經能接近傳統LRU 算法了。

https://redis.io/topics/lru-cache

問題:除了消耗資源之外,傳統LRU 還有什么問題?

如圖,假設A 在10 秒內被訪問了5 次,而B 在10 秒內被訪問了3 次。因為B 最后一次被訪問的時間比A 要晚,在同等的情況下,A 反而先被回收。

問題:要實現基于訪問頻率的淘汰機制,怎么做?

LFU

server.h

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;

當這24 bits 用作LFU 時,其被分為兩部分:

高16 位用來記錄訪問時間(單位為分鐘,ldt,last decrement time)

低8 位用來記錄訪問頻率,簡稱counter(logc,logistic counter)

counter 是用基于概率的對數計數器實現的,8 位可以表示百萬次的訪問頻率。

對象被讀寫的時候,lfu 的值會被更新。

db.c——lookupKey

void updateLFU(robj *val) {unsigned long counter = LFUDecrAndReturn(val);counter = LFULogIncr(counter);val->lru = (LFUGetTimeInMinutes()<<8) | counter; }

增長的速率由,lfu-log-factor 越大,counter 增長的越慢

redis.conf 配置文件

# lfu-log-factor 10

如果計數器只會遞增不會遞減,也不能體現對象的熱度。沒有被訪問的時候,計數器怎么遞減呢?

減少的值由衰減因子lfu-decay-time(分鐘)來控制,如果值是1 的話,N 分鐘沒有訪問就要減少N。

redis.conf 配置文件

# lfu-decay-time 1

?

總結

以上是生活随笔為你收集整理的Redis LRU 淘汰原理的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。