redis的key 有三種過期處理策略
1 惰性刪除過期key
優(yōu)點:
刪除操作消耗時間短,不影響性能
缺點:
如果大量過期的鍵從此不被訪問會浪費大量內(nèi)存
2 主動刪除過期key(定期)
優(yōu)點:
解決如果大量過期的鍵從此不被訪問會浪費大量內(nèi)存
缺點:
如果大量過期鍵會浪費很多時間,影響性能
3 lru策略刪除設(shè)置過期時間的key
優(yōu)點:
解決如果大量設(shè)置了過期時間的key而且內(nèi)存開銷達到maxmemory的問題
缺點:
如果大量設(shè)置了過期時間的key且沒達到maxmemory會浪費大量內(nèi)存
reids command相關(guān)代碼
void initServerConfig(void) {
...
populateCommandTable();
...
}
void populateCommandTable(void) {
int numcommands = sizeof(redisCommandTable)/sizeof(struct redisCommand);
for (j = 0; j < numcommands; j++) {
struct redisCommand *c = redisCommandTable+j;
char *f = c->sflags;
int retval1, retval2;
...
retval1 = dictAdd(server.commands, sdsnew(c->name), c);
rename前的初始命令
retval2 = dictAdd(server.orig_commands, sdsnew(c->name), c);
}
}
struct redisCommand redisCommandTable[] = {
{"module",moduleCommand,-2,"as",0,NULL,0,0,0,0,0},
{"get",getCommand,2,"rF",0,NULL,1,1,1,0,0},
{"set",setCommand,-3,"wm",0,NULL,1,1,1,0,0},
{"setnx",setnxCommand,3,"wmF",0,NULL,1,1,1,0,0},
{"setex",setexCommand,4,"wm",0,NULL,1,1,1,0,0},
...
}
設(shè)置key 超時時間
void setCommand(client *c) {
...
setGenericCommand(c,flags,c->argv[1],c->argv[2],expire,unit,NULL,NULL);
...
}
void setGenericCommand(client *c, int flags, robj *key, robj *val, robj *expire, int unit, robj *ok_reply, robj *abort_reply) {
if (expire) setExpire(c,c->db,key,mstime()+milliseconds);
}
void setExpire(client *c, redisDb *db, robj *key, long long when) {
...
kde = dictFind(db->dict,key->ptr);
de = dictAddOrFind(db->expires,dictGetKey(kde));
dictSetSignedIntegerVal(de,when);
...
}
惰性刪除
void getCommand(client *c) {
getGenericCommand(c);
}
int getGenericCommand(client *c) {
...
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL)
...
}
robj *lookupKeyReadOrReply(client *c, robj *key, robj *reply) {
robj *o = lookupKeyRead(c->db, key);
if (!o) addReply(c,reply);
return o;
}
robj *lookupKeyRead(redisDb *db, robj *key) {
return lookupKeyReadWithFlags(db,key,LOOKUP_NONE);
}
robj *lookupKeyReadWithFlags(redisDb *db, robj *key, int flags) {
...
if (expireIfNeeded(db,key) == 1) {
...
}
...
}
int expireIfNeeded(redisDb *db, robj *key) {
if (!keyIsExpired(db,key)) return 0;
...
刪除
return server.lazyfree_lazy_expire ? dbAsyncDelete(db,key) :
dbSyncDelete(db,key);
}
int keyIsExpired(redisDb *db, robj *key) {
mstime_t when = getExpire(db,key);
if (when < 0) return 0;
mstime_t now = server.lua_caller ? server.lua_time_start : mstime();
return now > when;
}
刪除方法
int dbSyncDelete(redisDb *db, robj *key) {
/* Deleting an entry from the expires dict will not free the sds of
* the key, because it is shared with the main dictionary. */
if (dictSize(db->expires) > 0) dictDelete(db->expires,key->ptr);
if (dictDelete(db->dict,key->ptr) == DICT_OK) {
if (server.cluster_enabled) slotToKeyDel(key);
return 1;
} else {
return 0;
}
}
主動刪除
void initServer(void) {
if (aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL) == AE_ERR) {
serverPanic("Can't create event loop timers.");
exit(1);
}
}
int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
databasesCron();
}
void databasesCron(void) {
/* Expire keys by random sampling. Not required for slaves
* as master will synthesize DELs for us. */
if (server.active_expire_enabled && server.masterhost == NULL) {
activeExpireCycle(ACTIVE_EXPIRE_CYCLE_SLOW);
} else if (server.masterhost != NULL) {
expireSlaveKeys();
}
...
}
void activeExpireCycle(int type) {
...
while (num--) {
dictEntry *de;
long long ttl;
隨機挑選一個key
if ((de = dictGetRandomKey(db->expires)) == NULL) break;
ttl = dictGetSignedIntegerVal(de)-now;
主動刪除
if (activeExpireCycleTryExpire(db,de,now)) expired++;
if (ttl > 0) {
/* We want the average TTL of keys yet not expired. */
ttl_sum += ttl;
ttl_samples++;
}
total_sampled++;
}
...
}
int activeExpireCycleTryExpire(redisDb *db, dictEntry *de, long long now) {
long long t = dictGetSignedIntegerVal(de);
if (now > t) {
sds key = dictGetKey(de);
robj *keyobj = createStringObject(key,sdslen(key));
propagateExpire(db,keyobj,server.lazyfree_lazy_expire);
if (server.lazyfree_lazy_expire)
dbAsyncDelete(db,keyobj);
else
dbSyncDelete(db,keyobj);
notifyKeyspaceEvent(NOTIFY_EXPIRED,
"expired",keyobj,db->id);
decrRefCount(keyobj);
server.stat_expiredkeys++;
return 1;
} else {
return 0;
}
}
lru策略刪除設(shè)置了過期時間的key
int processCommand(client *c) {
...
if (server.maxmemory && !server.lua_timedout) {
int out_of_memory = freeMemoryIfNeededAndSafe() == C_ERR;
}
...
}
int freeMemoryIfNeededAndSafe(void) {
if (server.lua_timedout || server.loading) return C_OK;
return freeMemoryIfNeeded();
}
int freeMemoryIfNeeded(void) {
...
while (mem_freed < mem_tofree) {
...
這里截選其中一種lru策略
else if (server.maxmemory_policy == MAXMEMORY_ALLKEYS_RANDOM ||
server.maxmemory_policy == MAXMEMORY_VOLATILE_RANDOM)
{
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) {
...
if (server.lazyfree_lazy_eviction)
dbAsyncDelete(db,keyobj);
else
dbSyncDelete(db,keyobj);
}
}
...
}