【Redis5源碼學習】淺析redis命令之persist篇

Grapehtml


命令語法

命令含義:移除給定key的生存時間,將這個 key 從『易失的』(帶生存時間 key )轉換成『持久的』(一個不帶生存時間、永不過時的 key )。
命令格式:redis

PERSIST key

命令實戰:segmentfault

redis> SET mykey "Hello"
OK
redis> EXPIRE mykey 10
(integer) 1
redis> TTL mykey
(integer) 10
redis> PERSIST mykey
(integer) 1
redis> TTL mykey
(integer) -1
redis>

返回值:
當生存時間移除成功時,返回 1 .
若是 key 不存在或 key 沒有設置生存時間,返回 0 .ide

源碼分析

命令入口:

/* PERSIST key */
void persistCommand(client *c) {
    //查找這個key,調用lookupKeyWrite函數
    if (lookupKeyWrite(c->db,c->argv[1])) {
            //若是這個key存在,調用刪除函數,調用dictGenericDelete函數
        if (removeExpire(c->db,c->argv[1])) {
            addReply(c,shared.cone);
            server.dirty++;
        } else {
            addReply(c,shared.czero);
        }
    } else {
        addReply(c,shared.czero);
    }
}

此處是persist命令的入口,咱們能夠看到這個命令在執行的過程當中有一下幾個階段:1.查找你要執行persist命令的key,看是否存在,若是不存在則直接返回客戶端信息。若是存在,這調用刪除函數移除過時時間,刪除成功返回給客戶端成功信息,aof標誌加1。函數

判斷key的可用性

/* Lookup a key for write operations, and as a side effect, if needed, expires
 * the key if its TTL is reached.
 *
 * Returns the linked value object if the key exists or NULL if the key
 * does not exist in the specified DB. */
/* 查找這個key
*/
robj *lookupKeyWrite(redisDb *db, robj *key) {
    //首先查找這個key是否過時,過時則刪除,此函數在以前的幾篇文章中已經介紹過,此處不在贅述。
    expireIfNeeded(db,key);
    在上一個步驟的基礎上,若是此鍵被刪除則返回null,不然返回這個鍵的值
    return lookupKey(db,key,LOOKUP_NONE);
}
int removeExpire(redisDb *db, robj *key) {
    /* An expire may only be removed if there is a corresponding entry in the
     * main dict. Otherwise, the key will never be freed. */
    serverAssertWithInfo(NULL,key,dictFind(db->dict,key->ptr) != NULL);
    return dictDelete(db->expires,key->ptr) == DICT_OK;
}

這個過程是在判斷這個key是否存在,它分爲兩個部分,首先他會查找這個key是否過時,若是過時則刪除,而後去查詢這個key,反之直接查找這個key,此處又一個疑問,爲何不是先查找這個key是否存在再去判斷是否過時?個人想法:redis中存儲過時時間和key-value是兩個字典,在expire字典中存在的值必定在key-value字典中存在,那麼在expire過時的時候就涉及reids的惰性刪除機制,爲了知足這個機制,reids在設計的時候會先對key的過時時間作判斷,而後再去判斷這個key是否存在,此時若是對redis的刪除機制感興趣的話,請查閱關於Redis數據過時策略源碼分析

真正移除的過程

/* Search and remove an element. This is an helper function for
 * dictDelete() and dictUnlink(), please check the top comment
 * of those functions. */
static dictEntry *dictGenericDelete(dict *d, const void *key, int nofree) {
    uint64_t h, idx;
    dictEntry *he, *prevHe;
    int table;
    
    //咱們知道在redis中的兩個ht,ht[0]和ht[1],若是這兩個ht中沒有已使用空間,直接return null
    if (d->ht[0].used == 0 && d->ht[1].used == 0) return NULL;
    if (dictIsRehashing(d)) _dictRehashStep(d);
    //根據key來獲取他的hash值
    h = dictHashKey(d, key);
    //遍歷ht[0]和ht[1],若是找到則刪除他的key-value,此處由於是刪除過時時間,咱們能夠看到外層調用函數傳入的是db->expires和key,則此塊是從expire的字典中刪除,            for語句中是刪除的過程,很簡單,遍歷,若是找到了刪除,同時釋放空間。
    for (table = 0; table <= 1; table++) {
        idx = h & d->ht[table].sizemask;
        he = d->ht[table].table[idx];
        prevHe = NULL;
        while(he) {
            if (key==he->key || dictCompareKeys(d, key, he->key)) {
                /* Unlink the element from the list */
                if (prevHe)
                    prevHe->next = he->next;
                else
                    d->ht[table].table[idx] = he->next;
                if (!nofree) {
                    dictFreeKey(d, he);
                    dictFreeVal(d, he);
                    zfree(he);
                }
                d->ht[table].used--;
                return he;
            }
            prevHe = he;
            he = he->next;
        }
        if (!dictIsRehashing(d)) break;
    }
    return NULL; /* not found */
}

此處是redis真正此處過時過時時間的步驟,此處只要設計dict的一些操做,例如遍歷dict的ht[0],ht[1]和刪除一個dict的值。大體的思路是先找到這個key而後進行釋放。由於此處在前面文章已經有所描述,故直接放上傳送門:dict是如何find一個元素的ui

相關文章
相關標籤/搜索