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

22次阅读

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

baiyan

命令语法

命令含义:将 key 改名为 newkey
命令格式:

RENAME key newkey

命令实战:

127.0.0.1:6379> keys *
1) "kkk"
2) "key1"
127.0.0.1:6379> rename kkk key1
OK
127.0.0.1:6379> keys *
1) "key1"
127.0.0.1:6379> rename kkk kkk
(error) ERR no such key

返回值:改名成功时提示 OK,失败时候返回一个错误

源码分析

主要流程

rename 命令的处理函数是 renameCommand():

void renameCommand(client *c) {renameGenericCommand(c,0);
}

renameCommand() 函数调用了底层通用重命名函数:

void renameGenericCommand(client *c, int nx) {
    robj *o;
    long long expire;
    int samekey = 0;

    // 重命名之前和之后的键名相同,置 samekey 标志为 1,不做处理
    if (sdscmp(c->argv[1]->ptr,c->argv[2]->ptr) == 0) samekey = 1;

    // 如果重命名之前的键不存在,直接返回
    if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.nokeyerr)) == NULL)
        return;
        
    // 如果置了 samekey 标志为 1,代表重命名前后的键名相同,那么什么都不做,直接返回 OK
    if (samekey) {addReply(c,nx ? shared.czero : shared.ok);
        return;
    }

    incrRefCount(o); // 由于查找到了 o,引用计数 ++
    expire = getExpire(c->db,c->argv[1]); // 获取重命名之前键的过期时间
    if (lookupKeyWrite(c->db,c->argv[2]) != NULL) {  // 如果重命名之后的键已经存在
        if (nx) { // 是否是执行的 renamenx 命令
            decrRefCount(o);
            addReply(c,shared.czero);
            return;
        }
        /* 重命名之后的键已经存在,需要删除这个已存在的键 */
        dbDelete(c->db,c->argv[2]);
    }
    dbAdd(c->db,c->argv[2],o); // 到这里重命名之后的键一定不存在了,可以添加这个键
    if (expire != -1) setExpire(c,c->db,c->argv[2],expire); // 如果之前设置了过期时间,同样给新键设置过期时间
    dbDelete(c->db,c->argv[1]); // 新键创建完毕,需删除之前的键
    signalModifiedKey(c->db,c->argv[1]); // 发出修改键信号
    signalModifiedKey(c->db,c->argv[2]); // 发出修改键信号
    notifyKeyspaceEvent(NOTIFY_GENERIC,"rename_from",
        c->argv[1],c->db->id); // 键空间事件触发
    notifyKeyspaceEvent(NOTIFY_GENERIC,"rename_to",
        c->argv[2],c->db->id); // 键空间事件触发
    server.dirty++;
    addReply(c,nx ? shared.cone : shared.ok);
}

我们首先整理一下这个命令的思路,如果让我们自己去实现重命名一个键这个命令,应该怎么做呢?
首先我们会判断一些边界条件。我们知道,键是放在键空间字典里的。假设我们现在有 key1-value1 和 key2-value2。现在需要把 key1 重命名为 key2,即重命名之后生成值为 key2-value1 的键值对。考虑以下边界情况:

  • 重命名之前的键名 key1 不存在
  • 重命名之后的键名 key2 已存在
  • 重命名之前为 key1,之后仍为 key1,即名称没有变化

对于第一种情况,如果重命名之前的键不存在,很简单,这种操作是不能够执行的。直接和客户端报告错误即可。
对于第二种情况,如果重命名之后的键已经存在了,redis 选择先查找出 value1,然后删掉 key2-value2 键值对。目前的变量状态仅仅有一个 value1,和一个我们输入的 key2。接下来,我们直接往数据库中添加 key2-value1 即可。最后删掉老的 key1-value1 键值对即可。
对于第三种情况,不作任何处理即可。

可能存在的问题

由于 redis 会在上面的第二种情况,即重命名之后的键存在的情况下,选择先后将 key2-value2、key1-value1 删除。所以,当碰到要删除的 key-value 对数据量非常大的时候,往往会造成单进程的 redis 的阻塞状态,造成对外服务的不可用。所以,往往在日常开发中,这个命令会被禁止使用,和之前讲过的 keys 命令有异曲同工之妙。在使用过程中我们需要格外注意。

正文完
 0