/* A redis object, that is a type able to hold a string / list / set *//* The actual Redis Object */#defineREDIS_LRU_BITS24#defineREDIS_LRU_CLOCK_MAX ((1<<REDIS_LRU_BITS)-1) /* Max value of obj->lru */#defineREDIS_LRU_CLOCK_RESOLUTION1000 /* LRU clock resolution in ms */typedefstruct redisObject {unsigned type:4;unsigned encoding:4;unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */int refcount;void*ptr;} robj;
/* Redis database representation. There are multiple databases identified * by integers from 0 (the default database) up to the max configured * database. The database number is the 'id' field in the structure. */typedefstruct redisDb { dict *dict; // 保存所有 key dict *expires; // 设置了过期时间的 key 及其过期时间 dict *blocking_keys; /* Keys with clients waiting for data (BLPOP) */ dict *ready_keys; /* Blocked keys that received a PUSH */ dict *watched_keys; /* WATCHED keys for MULTI/EXEC CAS */struct evictionPoolEntry *eviction_pool; /* Eviction pool of keys */int id; /* Database ID */longlong avg_ttl; /* Average TTL, just for stats */} redisDb;
intfreeMemoryIfNeeded(void) {if (mem_used <=server.maxmemory) return REDIS_OK; // 如果已经使用的没有达到上限,则直接返回okif (server.maxmemory_policy == REDIS_MAXMEMORY_NO_EVICTION) // NO EVICTION 策略,直接报错返回return REDIS_ERR; mem_tofree = mem_used -server.maxmemory; // 计算需要释放多少内存 mem_freed =0;latencyStartMonitor(latency);while (mem_freed < mem_tofree) { // 选择最合适的key释放内存直到达到了需要的内存为止int j, k, keys_freed =0;for (j =0; j <server.dbnum; j++) {long bestval =0; /* just to prevent warning */ sds bestkey =NULL; dictEntry *de; redisDb *db =server.db+j; dict *dict;// 判断淘汰策略是否与过期key有关,即从哪个哈希表找相应的key if (server.maxmemory_policy == REDIS_MAXMEMORY_ALLKEYS_LRU || server.maxmemory_policy == REDIS_MAXMEMORY_ALLKEYS_RANDOM) {
dict =server.db[j].dict; // ALLKEYS 类的策略查 dict } else { dict =server.db[j].expires; // VOTILE 类的策略查 expires }if (dictSize(dict)==0) continue;// RANDOM 类策略,就随机选 key 出来删除 if (server.maxmemory_policy == REDIS_MAXMEMORY_ALLKEYS_RANDOM || server.maxmemory_policy == REDIS_MAXMEMORY_VOLATILE_RANDOM) {
de =dictGetRandomKey(dict); bestkey =dictGetKey(de); }// LRU 类的策略 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; } } } }// TTL 类的策略elseif (server.maxmemory_policy == REDIS_MAXMEMORY_VOLATILE_TTL) {for (k =0; k <server.maxmemory_samples; k++) { sds thiskey;long thisval; de =dictGetRandomKey(dict); thiskey =dictGetKey(de); thisval = (long) dictGetVal(de); /* Expire sooner (minor expire unix timestamp) is better * candidate for deletion */if (bestkey ==NULL|| thisval < bestval) { bestkey = thiskey; bestval = thisval; } } }// 最后删除选择的key /* Finally remove the selected key. */if (bestkey) {longlong delta; robj *keyobj =createStringObject(bestkey,sdslen(bestkey));propagateExpire(db,keyobj); /* We compute the amount of memory freed by dbDelete() alone. * It is possible that actually the memory needed to propagate * the DEL in AOF and replication link is greater than the one * we are freeing removing the key, but we can't account for * that otherwise we would never exit the loop. * * AOF and Output buffer memory will be freed eventually so * we only care about memory used by the key space. */ delta = (longlong) zmalloc_used_memory();latencyStartMonitor(eviction_latency);dbDelete(db,keyobj);latencyEndMonitor(eviction_latency);latencyAddSampleIfNeeded("eviction-del",eviction_latency);latencyRemoveNestedEvent(latency,eviction_latency); delta -= (longlong) zmalloc_used_memory(); mem_freed += delta;server.stat_evictedkeys++;notifyKeyspaceEvent(REDIS_NOTIFY_EVICTED,"evicted", keyobj,db->id);decrRefCount(keyobj); keys_freed++; /* When the memory to free starts to be big enough, we may * start spending so much time here that is impossible to * deliver data to the slaves fast enough, so we force the * transmission here inside the loop. */if (slaves) flushSlavesOutputBuffers(); } }if (!keys_freed) {latencyEndMonitor(latency);latencyAddSampleIfNeeded("eviction-cycle",latency);return REDIS_ERR; /* nothing to free... */ } }latencyEndMonitor(latency);latencyAddSampleIfNeeded("eviction-cycle",latency);return REDIS_OK;}