探究redis和memcached的 LRU演算法--------redis的LRU的實現

來源:互聯網
上載者:User

標籤:

一直對這redis和memcached的兩個開源緩衝系統的LRU演算法感興趣。今天就打算總結一下這兩個LRU演算法的實現和區別。

首先要知道什麼是LRU演算法:LRU是Least Recently Used 近期最少使用演算法。相關的資料網上一大堆。http://en.wikipedia.org/wiki/Cache_algorithms#LRU  

redis的六種策略

rewriteConfigEnumOption(state,"maxmemory-policy",server.maxmemory_policy,        "volatile-lru", REDIS_MAXMEMORY_VOLATILE_LRU,        "allkeys-lru", REDIS_MAXMEMORY_ALLKEYS_LRU,        "volatile-random", REDIS_MAXMEMORY_VOLATILE_RANDOM,        "allkeys-random", REDIS_MAXMEMORY_ALLKEYS_RANDOM,        "volatile-ttl", REDIS_MAXMEMORY_VOLATILE_TTL,        "noeviction", REDIS_MAXMEMORY_NO_EVICTION,        NULL, REDIS_DEFAULT_MAXMEMORY_POLICY);

  1. volatile-lru:從已設定到期時間的資料集(server.db[i].expires)中挑選最近最少使用的資料淘汰
  2. volatile-ttl:從已設定到期時間的資料集(server.db[i].expires)中挑選將要到期的資料淘汰
  3. volatile-random:從已設定到期時間的資料集(server.db[i].expires)中任意選擇資料淘汰
  4. allkeys-lru:從資料集(server.db[i].dict)中挑選最近最少使用的資料淘汰
  5. allkeys-random:從資料集(server.db[i].dict)中任意選擇資料淘汰
  6. no-enviction(驅逐):禁止驅逐資料


而lru演算法經常是被用在緩衝系統,就比如我們的memcached和redis上。

首先介紹一下redis的。我去最近剛發布的redis3.0上搜尋一下代碼,通過它的代碼實現和如何?lru來講解。

/* The actual Redis Object */#define REDIS_LRU_BITS 24#define REDIS_LRU_CLOCK_MAX ((1<<REDIS_LRU_BITS)-1) /* Max value of obj->lru */#define REDIS_LRU_CLOCK_RESOLUTION 1000 /* LRU clock resolution in ms */typedef struct redisObject {    unsigned type:4;    unsigned encoding:4;    unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */    int refcount;    void *ptr;} robj;/* 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 function call. */#define LRU_CLOCK() ((1000/server.hz <= REDIS_LRU_CLOCK_RESOLUTION) ? server.lruclock : getLRUClock())

unsigned int getLRUClock(void) {    return (mstime()/REDIS_LRU_CLOCK_RESOLUTION) & REDIS_LRU_CLOCK_MAX;}


定義相關的一些lru的一些常量,並將

 unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */

講object的成員加入一個24位長度的lru成員。



我們發現使用最簡單粗暴的方式,就是和ALL -key機制是一樣,隨機選出16個。通過到期時間對pool記憶體資料進行淘汰。

  /* volatile-lru and allkeys-lru policy */            else if (server.maxmemory_policy == REDIS_MAXMEMORY_ALLKEYS_LRU ||                server.maxmemory_policy == REDIS_MAXMEMORY_VOLATILE_LRU)            {                struct evictionPoolEntry *pool = db->eviction_pool;                while(bestkey == NULL) {                    evictionPoolPopulate(dict, db->dict, db->eviction_pool);                    /* Go backward from best to worst element to evict. */                    for (k = REDIS_EVICTION_POOL_SIZE-1; k >= 0; k--) {                        if (pool[k].key == NULL) continue;                        de = dictFind(dict,pool[k].key);                        /* Remove the entry from the pool. */                        sdsfree(pool[k].key);                        /* Shift all elements on its right to left. */                        memmove(pool+k,pool+k+1,                            sizeof(pool[0])*(REDIS_EVICTION_POOL_SIZE-k-1));                        /* Clear the element on the right which is empty                         * since we shifted one position to the left.  */                        pool[REDIS_EVICTION_POOL_SIZE-1].key = NULL;                        pool[REDIS_EVICTION_POOL_SIZE-1].idle = 0;                        /* If the key exists, is our pick. Otherwise it is                         * a ghost and we need to try the next element. */                        if (de) {                            bestkey = dictGetKey(de);                            break;                        } else {                            /* Ghost... */                            continue;                        }                    }                }            }

通過lru來淘汰,返回lru時間,然後通過當前lru比較

/* 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) * REDIS_LRU_CLOCK_RESOLUTION;    } else {        return (lruclock + (REDIS_LRU_CLOCK_MAX - o->lru)) *                    REDIS_LRU_CLOCK_RESOLUTION;    }}


在servercron會定時更新起LRU的時間。隨機找到maxmemeory中的key,然後根據lru數值進行刪除。

因為

unsigned int dictGetSomeKeys(dict *d, dictEntry **des, unsigned int count) {

這個方法是通過隨機得來的資料。


總結:

redis只是每個Object維護一個相對的時間,淘汰時,隨機取3個或者更多的,找到最老的進行淘汰.這樣節省了雙鏈表的指標開銷,讀時還不用加鎖.雖不能保證一定淘汰最老的,但傾向於淘汰偏老的對象, 經過我們線上的實測:和標準的LRU對比,命中率的損失非常小, 效果不錯。


更多文章,歡迎訪問  http://blog.csdn.net/wallwind
















探究redis和memcached的 LRU演算法--------redis的LRU的實現

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.