【Redis】一组kv的存储

前置了解:

redisDb是Redis存储kv对象的结构,是hash表

存储过程

以set k v命令为例:
当redis接收到该命令后

STEP1 参数解析

调用栈是readQueryFromClient -> processInputBuffer -> processMultibulkBuffer,在processMultibulkBuffer中会对每个参数调用createObject生成robj;

STEP2 命令执行

调用栈是processCommand -> call -> proc(此处为setcommand) -> setGenericCommand -> setKey,最终数据是在setKey中被存在db中

setKey函数源码如下:

void setKey(redisDb *db, robj *key, robj *val) {
    if (lookupKeyWrite(db,key) == NULL) {
        dbAdd(db,key,val);
    } else {
        dbOverwrite(db,key,val);
    }
    incrRefCount(val);
    removeExpire(db,key);
    signalModifiedKey(db,key);
}

可以看到,该函数会将k,v的robj指针传入。假设k之前不存在,则会调用dbAdd将其插入到db中。

STEP3 存入redisDb

dbAdd会将robj形式的k转换为sds类型;之后调用dictAdd,由dictAdd将一个sds类型的k和一个robj类型的v插入到db中。
具体操作为调用dictAddRaw函数生成一个dictEntry,并将dictEntry插入到根据key求的hash值索引对应的桶中。

void dbAdd(redisDb *db, robj *key, robj *val) {
    sds copy = sdsdup(key->ptr);
    int retval = dictAdd(db->dict, copy, val);

    serverAssertWithInfo(NULL,key,retval == DICT_OK);
    if (val->type == OBJ_LIST ||
        val->type == OBJ_ZSET)
        signalKeyAsReady(db, key);
    if (server.cluster_enabled) slotToKeyAdd(key);
}

细节说明

在第一步协议解析之后,redis会对每个参数生成对应的robj并且存储在redisClient的argv中,执行完命令之后会调用resetClient来释放这个argv中的每个robj,因此为了实现v对应的robj不会被释放掉,redis实现了对象引用计数:
在resetClient函数中:

void resetClient(client *c) {
    redisCommandProc *prevcmd = c->cmd ? c->cmd->proc : NULL;

    freeClientArgv(c);
    c->reqtype = 0;
    c->multibulklen = 0;
    c->bulklen = -1;

    /* We clear the ASKING flag as well if we are not inside a MULTI, and
     * if what we just executed is not the ASKING command itself. */
    if (!(c->flags & CLIENT_MULTI) && prevcmd != askingCommand)
        c->flags &= ~CLIENT_ASKING;

    /* Remove the CLIENT_REPLY_SKIP flag if any so that the reply
     * to the next command will be sent, but set the flag if the command
     * we just processed was "CLIENT REPLY SKIP". */
    c->flags &= ~CLIENT_REPLY_SKIP;
    if (c->flags & CLIENT_REPLY_SKIP_NEXT) {
        c->flags |= CLIENT_REPLY_SKIP;
        c->flags &= ~CLIENT_REPLY_SKIP_NEXT;
    }
}
static void freeClientArgv(client *c) {
    int j;
    for (j = 0; j < c->argc; j++)
        decrRefCount(c->argv[j]);
    c->argc = 0;
    c->cmd = NULL;
}
void decrRefCount(robj *o) {
    if (o->refcount == 1) {
        switch(o->type) {
        case OBJ_STRING: freeStringObject(o); break;
        case OBJ_LIST: freeListObject(o); break;
        case OBJ_SET: freeSetObject(o); break;
        case OBJ_ZSET: freeZsetObject(o); break;
        case OBJ_HASH: freeHashObject(o); break;
        case OBJ_MODULE: freeModuleObject(o); break;
        case OBJ_STREAM: freeStreamObject(o); break;
        default: serverPanic("Unknown object type"); break;
        }
        zfree(o);
    } else {
        if (o->refcount <= 0) serverPanic("decrRefCount against refcount <= 0");
        if (o->refcount != OBJ_SHARED_REFCOUNT) o->refcount--;
    }
}

resetClient调用freeClientArgv来释放argv中的每个robj,真正的操作只是减引用计数,只有为0时才真正释放。

因此,为了保证dictEntry中v的robj不被释放,setKey函数实现了对v的引用计数加1.
而k因为在dbAdd函数中新生成了一个sds,所以可以在resetClient函数里释放k的robj

参考链接:
redis 一组kv实际内存占用计算

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值