redis的LRU策略理解

 

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

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()函數增長額外的開銷。app

/* 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;
    }
}
相關文章
相關標籤/搜索