redis evict.c内存淘汰机制的源码分析

来源:互联网 发布:同花顺mac版功能全吗 编辑:程序博客网 时间:2024/06/14 16:05

           众所周知,redis是一个内存数据库,所有的键值对都是存储在内存中。当数据变多之后,由于内存有

限就要淘汰一些键值对,使得内存有足够的空间来保存新的键值对。在redis中,通过设置server.maxmemory

来限定内存的使用(server.maxmemory为0,不限制内存),到达server.maxmemory就会触发淘汰机制。redis

主要提供6种淘汰策略:

      1)volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰

      2)volatile-lfu:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最不常使用的数据淘汰

      3)volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰

      4)volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰

      4)allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰

      5)allkeys-lfu:从数据集(server.db[i].dict)中挑选最近最不常使用的数据淘汰

      6)allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰

      7)no-enviction(驱逐):禁止驱逐数据

redis在每次执行客户端的命令的时候都会检查使用内存是否超过server.maxmemory,如果超过就进行淘汰数据。

int processCommand(client *c) {……//server.maxmemory为0,表示对内存没有限制if (server.maxmemory) {//判断内存,进行内存淘汰        int retval = freeMemoryIfNeeded();        ……    }    ……}

int freeMemoryIfNeeded(void) {    mem_reported = zmalloc_used_memory();//获取redis内存使用    if (mem_reported <= server.maxmemory) return C_OK;     mem_used = mem_reported;    if (slaves) {        listRewind(server.slaves,&li);        while((ln = listNext(&li))) {            ……//减去slaves的output缓冲区        }    }//aof的缓冲区的内存使用    if (server.aof_state != AOF_OFF) {        mem_used -= sdslen(server.aof_buf);        mem_used -= aofRewriteBufferSize();    }    /* Check if we are still over the memory limit. */    if (mem_used <= server.maxmemory) return C_OK;    /* Compute how much memory we need to free. */    mem_tofree = mem_used - server.maxmemory;    mem_freed = 0;    if (server.maxmemory_policy == MAXMEMORY_NO_EVICTION)        goto cant_free; /* 禁止驱逐数据 */    //进行数据驱逐    while (mem_freed < mem_tofree) {    ……    sds bestkey = NULL;        if (server.maxmemory_policy & (MAXMEMORY_FLAG_LRU|MAXMEMORY_FLAG_LFU) ||            server.maxmemory_policy == MAXMEMORY_VOLATILE_TTL)        {   //进行ttl或者lru淘汰机制            struct evictionPoolEntry *pool = EvictionPoolLRU;            while(bestkey == NULL) {                unsigned long total_keys = 0, keys;                for (i = 0; i < server.dbnum; i++) {                    db = server.db+i;                    dict = (server.maxmemory_policy & MAXMEMORY_FLAG_ALLKEYS) ?                            db->dict : db->expires;                    if ((keys = dictSize(dict)) != 0) {                        evictionPoolPopulate(i, dict, db->dict, pool);                        //pool根据机制构建的evictionPool                    }                }/*在evictionPool中从后往前选择一个还在存在数据库中的键值进行驱逐*/                for (k = EVPOOL_SIZE-1; k >= 0; k--) {                    if (pool[k].key == NULL) continue;                    bestdbid = pool[k].dbid;                    if (server.maxmemory_policy & MAXMEMORY_FLAG_ALLKEYS) {                        de = dictFind(server.db[pool[k].dbid].dict,                            pool[k].key);                    } else {                        de = dictFind(server.db[pool[k].dbid].expires,                            pool[k].key);                    }                    ……                    if (de) {                        bestkey = dictGetKey(de);                        break;                    } else {                        /* Ghost... Iterate again. */                    }                }            }        }        else if (server.maxmemory_policy == MAXMEMORY_ALLKEYS_RANDOM ||                 server.maxmemory_policy == MAXMEMORY_VOLATILE_RANDOM)        {   /* 从db->dict或者db->expires随机选择一个键值对进行淘汰*/            for (i = 0; i < server.dbnum; i++) {                j = (++next_db) % server.dbnum;                db = server.db+j;                dict = (server.maxmemory_policy == MAXMEMORY_ALLKEYS_RANDOM) ?                        db->dict : db->expires;                if (dictSize(dict) != 0) {                    de = dictGetRandomKey(dict);                    bestkey = dictGetKey(de);                    bestdbid = j;                    break;                }            }        }//驱逐选中的键值对        if (bestkey) {            db = server.db+bestdbid;            robj *keyobj = createStringObject(bestkey,sdslen(bestkey));            propagateExpire(db,keyobj,server.lazyfree_lazy_eviction);            delta = (long long) zmalloc_used_memory();            if (server.lazyfree_lazy_eviction)                dbAsyncDelete(db,keyobj);            else                dbSyncDelete(db,keyobj);            delta -= (long long) zmalloc_used_memory();            mem_freed += delta;            server.stat_evictedkeys++;            decrRefCount(keyobj);            keys_freed++;            if (slaves) flushSlavesOutputBuffers();        }    }    return C_OK;cant_free://进行内存空间的惰性释放    while(bioPendingJobsOfType(BIO_LAZY_FREE)) {        if (((mem_reported - zmalloc_used_memory()) + mem_freed) >= mem_tofree)            break;        usleep(1000);    }    return C_ERR;}

根据淘汰机制从随机选取的键值对中选取键值对构建evictionPool

1)LRU数据淘汰机制:在数据集中随机选取几个键值对,选择lru最大的一部分键值对构建evictionPool

2)LFU数据淘汰机制:在数据集中随机选取几个键值对,选择lfu最小的一部分键值对构建evictionPool

3)TTL数据淘汰机制:从设置过期时间的数据集中随机选取几个键值对,选择TTL最大的一部分键值对构建evictionPool

void evictionPoolPopulate(int dbid, dict *sampledict, dict *keydict, struct evictionPoolEntry *pool) {    int j, k, count;    dictEntry *samples[server.maxmemory_samples];    //从数据集sampledict随机选取键值对    count = dictGetSomeKeys(sampledict,samples,server.maxmemory_samples);    for (j = 0; j < count; j++) {        de = samples[j];        key = dictGetKey(de);        if (server.maxmemory_policy != MAXMEMORY_VOLATILE_TTL) {            if (sampledict != keydict) de = dictFind(keydict, key);            o = dictGetVal(de);        }                if (server.maxmemory_policy & MAXMEMORY_FLAG_LRU) {            idle = estimateObjectIdleTime(o);//LRU机制,计算lru值        } else if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {            idle = 255-LFUDecrAndReturn(o);//LFU机制,计算lfu值        } else if (server.maxmemory_policy == MAXMEMORY_VOLATILE_TTL) {            idle = ULLONG_MAX - (long)dictGetVal(de);//TTL机制,计算ttl值        }        k = 0;        //根据idle从小到大将键值对插入到pool(插入排序的机制),但只保留idle最大的EVPOOL_SIZE个        while (k < EVPOOL_SIZE &&pool[k].key &&pool[k].idle < idle)         k++;        if (k == 0 && pool[EVPOOL_SIZE-1].key != NULL) {            continue;        } else if (k < EVPOOL_SIZE && pool[k].key == NULL) {            /* Inserting into empty position. No setup needed before insert. */        } else {            if (pool[EVPOOL_SIZE-1].key == NULL) {                sds cached = pool[EVPOOL_SIZE-1].cached;                memmove(pool+k+1,pool+k,sizeof(pool[0])*(EVPOOL_SIZE-k-1));                pool[k].cached = cached;            } else {                k--;                sds cached = pool[0].cached; /* Save SDS before overwriting. */                if (pool[0].key != pool[0].cached) sdsfree(pool[0].key);                memmove(pool,pool+1,sizeof(pool[0])*k);                pool[k].cached = cached;            }        }        int klen = sdslen(key);        if (klen > EVPOOL_CACHED_SDS_SIZE) {            pool[k].key = sdsdup(key);        } else {            memcpy(pool[k].cached,key,klen+1);            sdssetlen(pool[k].cached,klen);            pool[k].key = pool[k].cached;        }        pool[k].idle = idle;        pool[k].dbid = dbid;    }}


0 0
原创粉丝点击