解析Redis淘汰策略源码分析(redis淘汰策略源码)

Redis是一款非常流行的内存数据库,但是它也会面临内存不足的问题。为了防止内存溢出,Redis提供了五种不同的淘汰策略。在本文中,我们将解析Redis淘汰策略的源码,深入了解每种淘汰策略的工作原理。

1. noeviction

noeviction是Redis的默认淘汰策略,它不会删除任何键值对。当Redis内存空间使用完后,会导致新插入的数据无法存储,返回错误信息“OOM command not allowed when used memory > ‘maxmemory’”。下面是相关代码:

“`c

if (server.maxmemory_policy != MAXMEMORY_NO_EVICTION &&

free_memory

(stress_test || (mstime()-server.stat_starttime) > 5000))

{

int j, k, i, keys_freed = 0;

while(best effort to free memory);

}


2. volatile-lru

该策略会首先在所有设置过期时长的键值对中,按最近使用时间从旧到新进行排序,并删除最近最少使用的键值对。下面是相关代码:

```c
static int compare_chunk(const void *a, const void *b) {
serverAssert((uintptr_t)a % LRU_BITS_PER_CHUNK == 0);
serverAssert((uintptr_t)b % LRU_BITS_PER_CHUNK == 0);
return memcmp(a,b,LRU_BITS_PER_CHUNK);
}
static int keylm_compare(void *privdata, const void *a, const void *b) {
robj_roptr_wrapper *wa = (robj_roptr_wrapper*)a, *wb = (robj_roptr_wrapper*)b;
return compare_chunk(LRU_BITS(wa->o),LRU_BITS(wb->o));
}
static int evictKeyFromPool(dict *pool, int samples, dict **evicted) {
robj *keys[samples];
*evicted = dictCreate(&keylmDictType,NULL);
dictAdd(*evicted,keylm_sentinel,NULL);
dictEnableResize(*evicted);
dictSetHashFunctionSeed(*evicted,server.hash_seed);
int k = generatePool(pool,keys,samples);
qsort(keys,k,sizeof(robj_roptr_wrapper),keylm_compare);
for (int i = 0; i
if (dictSize(*evicted) == 1 || dictSize(pool)
if (dictGet(pool,keys[i]) == NULL) continue;
if (dictGetExpire(pool,keys[i]) == -1) continue;
/* Remove the key from the original pool and insert it into the
* "to be evicted" pool. */
dictAdd(*evicted,keys[i],NULL);
dictGet(pool,keys[i]);
dictDelete(pool,keys[i]);
activeDefragCycle();
}
return k;
}

3. volatile-ttl

该策略会首先在所有设置过期时长的键值对中,按过期时间从早到晚进行排序,并删除最早过期的键值对。下面是相关代码:

“`c

void volatileTTLCommand(client *c) {

long long ttl_sum = 0, keys = 0;

/* We use a blacklist approach where we start with a blacklist of just

* the ‘*’ character which means all keys are selected. We then remove

* keys from this preferred list if certn patterns are found in the

* key names. When we have no other patterns left, we are left with

* the preferred list of keys that we want to operate on. */

sds pattern = c->argc == 2 ? c->argv[1]->ptr : “*”;

sds prefix = NULL;

if (stringmatch(pattern,”*:*”,0))

prefix = sdscatlen(sdsempty(),pattern+2,sdslen(pattern)-2);

else if (stringmatch(pattern,”*”,0))

prefix = sdscatlen(sdsempty(),””,0);

else {

addReplyError(c,”Invalid pattern argument.”);

return;

}

/* Scan the keyspace to build the preferred list of keys that match

* the pattern. */

void incrRefCountIfNeeded(robj *o);

void *replylen = addDeferredMultiBulkLength(c);

scanGenericCommand(c,0,NULL,NULL,SCAN_SELECTCOUNT);

unsigned long numkeys = 0;

if (c->flags & CLIENT_MULTI_BLOCK) {

numkeys = addReplyDeferredMultiBulkLength(c,replylen);

while(c->flags & CLIENT_MULTI_BLOCK) {

sds *response = setDeferredMultiBulkClientReply(c,numkeys);

for (unsigned long j = 0; j reply->elements; j++) {

robj *key = c->reply->element[j];

long long ttl = -1;

if (dictFind(c->db->dict,key->ptr) != NULL &&

getExpire(c->db,key) != -1) {

ttl = getExpire(c->db,key)-mstime();

if (ttl

}

if (ttl >= 0) {

ttl_sum += ttl;

keys++;

/* Add the key to the preferred list for this operation. */

response[j] = key->ptr;

incrRefCountIfNeeded(key);

}

}

numkeys -= c->reply->elements;

addReplyMultiBulkLen(c,c->reply->elements);

setDeferredAggregateMultiBulkLength(c,replylen,c->reply->elements);

decrRefCount(c->reply);

if (numkeys) {

c->flags &= ~CLIENT_MULTI_BLOCK;

scanGenericCommand(c,numkeys,NULL,NULL,SCAN_SELECTCOUNT);

}

}

}

setDeferredLength(c,replylen,numkeys*2);

/* Operate on the preferred list of keys. */

if (keys) {

sds *keynames = zmalloc(sizeof(sds)*keys);

int n = 0;

/* Sort the keys array by TTL, from the nearest to the furthest

* deadline. */

for (unsigned long j = 0; j

sds key = c->reply[j]->ptr;

long long ttl = -1;

if (dictFind(c->db->dict,key) != NULL &&

getExpire(c->db,c->reply[j]) != -1) {

ttl = getExpire(c->db,c->reply[j])-mstime();

if (ttl

}

if (ttl != -1)

keynames[n++] = key;

}

qsort(keynames,n,sizeof(char*),(void*)sortCompareStringPtr);

/* Delete keys by the sorted TTL order. */

for (int j = 0; j

delKey(c->db,keynames[j]);

zfree(keynames);

}

/* Finally send the reply to the client. */

addReplyLongLong(c,keys ? ttl_sum/keys : -1);

setDeferredAggregateLength(c,replylen,3);

}


4. volatile-lfu

该策略会首先在所有设置过期时长的键值对中,按访问次数从低到高进行排序,并删除访问次数最少的键值对。下面是相关代码:

```c
void volatileLFUCommand(client *c) {
long long bestpop = 0, keys = 0;

/* We use a blacklist approach where we start with a blacklist of just
* the '*' character which means all keys are selected. We then remove
* keys from this preferred list if certn patterns are found in the
* key names. When we have no other patterns left, we are left with
* the preferred list of keys that we want to operate on. */
sds pattern = c->argc == 2 ? c->argv[1]->ptr : "*";
sds prefix = NULL;
if (stringmatch(pattern,"*:*",0))
prefix = sdscatlen(sdsempty(),pattern+2,sdslen(pattern)-2);
else if (stringmatch(pattern,"*",0))
prefix = sdscatlen(sdsempty(),"",0);
else {
addReplyError(c,"Invalid pattern argument.");
return;
}

/* Scan the keyspace to build the preferred list of keys that match
* the pattern. */
void incrRefCountIfNeeded(robj *o);
void *replylen = addDeferredMultiBulkLength(c);
scanGenericCommand(c,0,NULL,NULL,SCAN_SELECTCOUNT);
unsigned long numkeys = 0;
if (c->flags & CLIENT_MULTI_BLOCK) {
numkeys = addReplyDeferredMultiBulkLength(c,replylen);
while(c->flags & CLIENT_MULTI_BLOCK) {
sds *response = setDeferredMultiBulkClientReply(c,numkeys);
for (unsigned long j = 0; j reply->elements; j++) {


数据运维技术 » 解析Redis淘汰策略源码分析(redis淘汰策略源码)