redis的LRU策略理解


 

首先看下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;
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM