Redis5源码学习浅析redis命令之restore篇

9次阅读

共计 3160 个字符,预计需要花费 8 分钟才能阅读完成。

Grape


命令语法

命令含义:反序列化给定的序列化值,并将它和给定的 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,否则返回一个错误。

源码分析

源码分析部分我们分为几个部分来讲解。

参数处理

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 是首先淘汰一定时期内被访问次数最少的页!

算法根据数据的历史访问频率来淘汰数据,其核心思想是“如果数据过去被访问多次,那么将来被访问的频率也更高”。
具体的算法流程如下所示:

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

Least Recently Used algorithm.LRU 是首先淘汰最长时间未被使用的页面!

算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。
具体的算法流程如下所示:

FIFO 先进先出置换算法

FIFO(First in First out),先进先出。
其实在操作系统的设计理念中很多地方都利用到了先进先出的思想,比如作业调度(先来先服务),为什么这个原则在很多地方都会用到呢?因为这个原则简单、且符合人们的惯性思维,具备公平性,并且实现起来简单,直接使用数据结构中的队列即可实现。

正文完
 0