1. 程式人生 > >redis的LRU策略理解

redis的LRU策略理解

判斷 lag frequency actually nds 次循環 mac 例如 cron

首先看下serverCron中,服務器每次循環執行的時候,都會刷新server.lrulock。

int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
    ...
    server.lruclock = getLRUClock();
   ...

使用的方法是getLRUClock,LRU_CLOCK_RESOLUTION代表LRU算法的精度,即一個LRU的單位是多長時間。LRU_CLOCK_MAX代表邏輯時鐘的最大位數,類似現實中的表盤,劃分了最大的刻度,一個刻度是一個LRU大小。所以整個方法表示的含義為:當前時間是LRU的單位的多少倍,即已經過了多少個LRU,然後對最大刻度LRU_CLOCK_MAX取模。

unsigned int getLRUClock(void) {
    return (mstime()/LRU_CLOCK_RESOLUTION) & LRU_CLOCK_MAX;
}

了解以上概念後,再看到創建object對象的時候,會給其lru屬性賦值,下次被訪問時也會更新。

robj *createObject(int type, void *ptr) {
    robj *o = zmalloc(sizeof(*o));
    o->type = type;
    o->encoding = OBJ_ENCODING_RAW;
    o->ptr = ptr;
    o
->refcount = 1; /* Set the LRU to the current lruclock (minutes resolution). */ o->lru = LRU_CLOCK(); return o; }

/* Low level key lookup API, not actually called directly from commands
 * implementations that should instead rely on lookupKeyRead(),
 * lookupKeyWrite() and lookupKeyReadWithFlags(). 
*/ robj *lookupKey(redisDb *db, robj *key, int flags) { dictEntry *de = dictFind(db->dict,key->ptr); if (de) { robj *val = dictGetVal(de); /* Update the access time for the ageing algorithm. * Don‘t do it if we have a saving child, as this will trigger * a copy on write madness. */ if (server.rdb_child_pid == -1 && server.aof_child_pid == -1 && !(flags & LOOKUP_NOTOUCH)) { val->lru = LRU_CLOCK(); } return val; } else { return NULL; } }

使用的方法是LRU_CLOCK,server.hz代表服務器刷新的頻率,意思是如果服務器的時間更新精度值比LRU的精度值要小(精度值表示一次刷新的間隔時間,越小精度越高),說明服務器的精度更高,直接用服務器的時間。舉例如果服務器精度是10ms, LRU精度是100ms,則在100ms內服務器進行10次刷新,得到的server.lrulock都是一樣,既然如此,不必調用getLRUCLOCK()函數增加額外的開銷。

/* Macro used to obtain the current LRU clock.
 * If the current resolution is lower than the frequency we refresh the
 * LRU clock (as it should be in production servers) we return the
 * precomputed value, otherwise we need to resort to a system call. */
#define LRU_CLOCK() ((1000/server.hz <= LRU_CLOCK_RESOLUTION) ? server.lruclock : getLRUClock())

那麽obj的lru值是如何使用的呢,判斷一個key是否需要過期淘汰時,先計算其最近沒有使用的時間,方法如下:獲取當前時鐘,如果obj的lru小於當前時鐘,則獲取obj到當前時鐘間隔了多少個LRU單位時間,再乘以LRU_CLOCK_RESOLUTION即得到真實毫秒數。

/* 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;
    }
}

redis的LRU策略理解