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

Graperedis


命令語法

命令含義:反序列化給定的序列化值,並將它和給定的 key 關聯。算法

命令格式:

RESTORE key ttl serialized-value [REPLACE] [ABSTTL] [IDLETIME seconds] [FREQ frequency]

命令實戰

redis> DEL mykey
0
redis> RESTORE mykey 0 "\n\x17\x17\x00\x00\x00\x12\x00\x00\x00\x03\x00\
                        x00\xc0\x01\x00\x04\xc0\x02\x00\x04\xc0\x03\x00\
                        xff\x04\x00u#<\xc0;.\xe9\xdd"
OK
redis> TYPE mykey
list
redis> LRANGE mykey 0 -1
1) "1"
2) "2"
3) "3"

返回值

若是反序列化成功那麼返回 OK ,不然返回一個錯誤。數據結構

源碼分析

源碼分析部分咱們分爲幾個部分來說解。ide

參數處理

void restoreCommand(client *c) {
    long long ttl, lfu_freq = -1, lru_idle = -1, lru_clock = -1;
    rio payload;
    int j, type, replace = 0, absttl = 0;
    robj *obj;
    /* 解析參數 */
    for (j = 4; j < c->argc; j++) {
        int additional = c->argc-j-1;
        if (!strcasecmp(c->argv[j]->ptr,"replace")) {
            replace = 1;
        } else if (!strcasecmp(c->argv[j]->ptr,"absttl")) {
            absttl = 1;
        } else if (!strcasecmp(c->argv[j]->ptr,"idletime") && additional >= 1 &&
                   lfu_freq == -1)
        {
            if (getLongLongFromObjectOrReply(c,c->argv[j+1],&lru_idle,NULL)
                    != C_OK) return;
            if (lru_idle < 0) {
                addReplyError(c,"Invalid IDLETIME value, must be >= 0");
                return;
            }
            lru_clock = LRU_CLOCK();
            j++; /* Consume additional arg. */
        } else if (!strcasecmp(c->argv[j]->ptr,"freq") && additional >= 1 &&
                   lru_idle == -1)
        {
            if (getLongLongFromObjectOrReply(c,c->argv[j+1],&lfu_freq,NULL)
                    != C_OK) return;
            if (lfu_freq < 0 || lfu_freq > 255) {
                addReplyError(c,"Invalid FREQ value, must be >= 0 and <= 255");
                return;
            }
            j++; /* Consume additional arg. */
        } else {
            addReply(c,shared.syntaxerr);
            return;
        }
    }

在上邊咱們提到了restore命令格式,咱們能夠看到,在第四個參數開始都是可選參數,因此解析參數中是從j=4開始遍歷的,在遍歷的過程當中會根據不一樣的參數作不一樣的操做。
咱們依次來看下這四個命令:源碼分析

  1. Replace:判斷若是是replace字段,將標識位replace置爲1。
  2. Absttl: 判斷若是是absttl字段,將標識位absttl置爲1。若是使用了ABSTTL修飾符,ttl則應表示密鑰將在其中終止的絕對 Unix時間戳(以毫秒爲單位)。
  3. Idletime&&freq: 這兩個參數在object命令中有很詳細的解釋。在objec中,ideltime是返回自存儲在指定鍵處的對象處於空閒狀態以來的秒數(讀或寫操做未請求)。freq是返回存儲在指定鍵處的對象的對數訪問頻率計數器。在這段命令中解析到這兩個命令時,ideltime設置了lru_clock時鐘值。在freq設置lru_freq,設置頻率且判斷是否在0-255之間。

校驗&&對應模式操做

/* 此處是確保這個key是否存在,這個操做僅在replace等於0的時候進行*/
    if (!replace && lookupKeyWrite(c->db,c->argv[1]) != NULL) {
        addReply(c,shared.busykeyerr);
        return;
    }
        
    /* 檢查ttl合法,規則是是否小於0且是否可轉爲數字*/
    if (getLongLongFromObjectOrReply(c,c->argv[2],&ttl,NULL) != C_OK) {
        return;
    } else if (ttl < 0) {
        addReplyError(c,"Invalid TTL value, must be >= 0");
        return;
    }
    // 檢查RDB版本和數據校驗和。若是它們不匹配,則返回錯誤。
    if (verifyDumpPayload(c->argv[3]->ptr,sdslen(c->argv[3]->ptr)) == C_ERR)
    {
        addReplyError(c,"DUMP payload version or checksum are wrong");
        return;
    }
    rioInitWithBuffer(&payload,c->argv[3]->ptr);
    if (((type = rdbLoadObjectType(&payload)) == -1) ||
        ((obj = rdbLoadObject(type,&payload)) == NULL))
    {
        addReplyError(c,"Bad data format");
        return;
    }
    // 若是是replace模式,刪除key
    if (replace) dbDelete(c->db,c->argv[1]);

新增key

/* Create the key and set the TTL if any */
    dbAdd(c->db,c->argv[1],obj);  //若是key存在,則報錯
    if (ttl) {
        if (!absttl) ttl+=mstime();
        setExpire(c,c->db,c->argv[1],ttl);
    }
    //建立新的key。
    //若是存在ttl就設置.
    // 若是ttl爲0,則建立密鑰時不會有任何過時,不然將設置指定的過時時間(以毫秒爲單位)
    objectSetLRUOrLFU(obj,lfu_freq,lru_idle,lru_clock);
    //設置頻率和時間範圍限制值
    signalModifiedKey(c->db,c->argv[1]);
    addReply(c,shared.ok);
    server.dirty++;
}

拓展

LFU 最近最不經常使用頁面置換算法

Least Frequently Used algorithm.LFU是首先淘汰必定時期內被訪問次數最少的頁!spa

算法根據數據的歷史訪問頻率來淘汰數據,其核心思想是「若是數據過去被訪問屢次,那麼未來被訪問的頻率也更高」。
具體的算法流程以下所示:操作系統

clipboard.png

LRU 最近最近最久未使用置換算法

Least Recently Used algorithm.LRU是首先淘汰最長時間未被使用的頁面!設計

算法根據數據的歷史訪問記錄來進行淘汰數據,其核心思想是「若是數據最近被訪問過,那麼未來被訪問的概率也更高」。
具體的算法流程以下所示:
clipboard.pngrest

FIFO 先進先出置換算法

FIFO(First in First out),先進先出。
其實在操做系統的設計理念中不少地方都利用到了先進先出的思想,好比做業調度(先來先服務),爲何這個原則在不少地方都會用到呢?由於這個原則簡單、且符合人們的慣性思惟,具有公平性,而且實現起來簡單,直接使用數據結構中的隊列便可實現。code

相關文章
相關標籤/搜索