redis key 過期處理策略

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);
      }
}
...
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子辫愉,更是在濱河造成了極大的恐慌走哺,老刑警劉巖裤唠,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機趣兄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來悼嫉,“玉大人艇潭,你說我怎么就攤上這事∠访铮” “怎么了蹋凝?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長总棵。 經(jīng)常有香客問我鳍寂,道長,這世上最難降的妖魔是什么彻舰? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任伐割,我火速辦了婚禮,結(jié)果婚禮上刃唤,老公的妹妹穿的比我還像新娘。我一直安慰自己白群,他們只是感情好尚胞,可當(dāng)我...
    茶點故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著帜慢,像睡著了一般笼裳。 火紅的嫁衣襯著肌膚如雪唯卖。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天躬柬,我揣著相機與錄音拜轨,去河邊找鬼。 笑死允青,一個胖子當(dāng)著我的面吹牛橄碾,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播颠锉,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼法牲,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了琼掠?” 一聲冷哼從身側(cè)響起拒垃,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎瓷蛙,沒想到半個月后悼瓮,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡艰猬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年谤牡,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片姥宝。...
    茶點故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡翅萤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出腊满,到底是詐尸還是另有隱情套么,我是刑警寧澤,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布碳蛋,位于F島的核電站胚泌,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏肃弟。R本人自食惡果不足惜玷室,卻給世界環(huán)境...
    茶點故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望笤受。 院中可真熱鬧穷缤,春花似錦、人聲如沸箩兽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽汗贫。三九已至身坐,卻和暖如春秸脱,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背部蛇。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工摊唇, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人涯鲁。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓巷查,卻偏偏與公主長得像,于是被迫代替她去往敵國和親撮竿。 傳聞我的和親對象是個殘疾皇子吮便,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,092評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 一、踏坑事件 時間:2017年10月23日 凌晨 背景:個人賽中用戶可以發(fā)起pk幢踏,pk即為兩個開播主播進行限時收禮...
    愛情小傻蛋閱讀 1,269評論 1 2
  • NOSQL類型簡介鍵值對:會使用到一個哈希表髓需,表中有一個特定的鍵和一個指針指向特定的數(shù)據(jù),如redis房蝉,volde...
    MicoCube閱讀 3,985評論 2 27
  • 文章已經(jīng)放到github上 僚匆,如果對您有幫助 請給個star[https://github.com/qqxuanl...
    尼爾君閱讀 2,287評論 0 22
  • 類型常量 對象的名稱REDIS_STRING 字符串對象REDIS_LIST 列表對象REDIS_H...
    青城樓主閱讀 215評論 0 1
  • 【本教程目錄】 1.redis是什么2.redis的作者3.誰在使用redis4.學(xué)會安裝redis5.學(xué)會啟動r...
    徐猿猿閱讀 1,870評論 0 35