redis源码分析--set命令

本文介绍了Redis中SET命令的工作原理及其对值的编码方式。根据值的大小不同,Redis会选择int编码、EMBSTR编码或raw编码来优化内存使用。文章详细解释了这些编码的实现细节,并展示了如何通过这些编码提高数据存储效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

单值的情况下可以使用set命令。
set key value
set命令在源码会直接调用setCommand

//set命令 set key value
void setCommand(client *c) {
    ....
   //值的编码
    c->argv[2] = tryObjectEncoding(c->argv[2]);  
    setGenericCommand(c,flags,c->argv[1],c->argv[2],expire,unit,NULL,NULL);
}

tryObjectEncoding会对传进来的值进行编码,如果小于20个字节采用int编码编码成64位,如果是小于44个字节会采用EMBSTR编码,比44字节大会采用raw编码
这三种编码模式的示意图:
在这里插入图片描述

int编码的具体实现:

//编码stirng类型目的是为了节省内存空间
robj *tryObjectEncoding(robj *o) {
    long value;
    sds s = o->ptr;
    size_t len;

  //确认是string类型
    serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);

    //如果不是RAW也不是EMBSTR编码,直接返回
    if (!sdsEncodedObject(o)) return o;

   //如果是共享的对象,直接返回
     if (o->refcount > 1) return o;

   //少于20个子节点时候采用INT编码为LONG整型
    len = sdslen(s);
    if (len <= 20 && string2l(s,len,&value)) {
      	........
    }

 //如果长度小于OBJ_ENCODING_EMBSTR_SIZE_LIMIT的RAW编码,
//采用EMBSTR编码会更高效
    if (len <= OBJ_ENCODING_EMBSTR_SIZE_LIMIT) {
        robj *emb;

        if (o->encoding == OBJ_ENCODING_EMBSTR) return o;
        emb = createEmbeddedStringObject(s,sdslen(s));
        decrRefCount(o);
        return emb;
    }

 //不能编码,直接返回
trimStringObjectIfNeeded(o);
 return o;
}

错误检查完成,把key value的键值对插入进对应db的字典里面

//把键值对添加进redis,是db的字典里面查找,如果没有直接添加进去,如果存在
//直接修改
void genericSetKey(client *c, redisDb *db, robj *key, robj *val, int keepttl, int signal) {
    if (lookupKeyWrite(db,key) == NULL) {
        dbAdd(db,key,val);
    } else {
        dbOverwrite(db,key,val);
    }
    incrRefCount(val);
    if (!keepttl) removeExpire(db,key);
    if (signal) signalModifiedKey(c,db,key);
}
//根据key查找redisobj对象
robj *lookupKey(redisDb *db, robj *key, int flags) {
    dictEntry *de = dictFind(db->dict,key->ptr);
    if (de) {
        robj *val = dictGetVal(de);

        /* Update the access time for the ageing algorithm.
         * Don't do it if we have a saving child, as this will trigger
         * a copy on write madness. */
        if (!hasActiveChildProcess() && !(flags & LOOKUP_NOTOUCH)){
            if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {
                updateLFU(val);
            } else {
                val->lru = LRU_CLOCK();
            }
        }
        return val;
    } else {
        return NULL;
    }
}

如果是一个10字节的整数,会采用INT整型编码,最后的使用的内存空间会变成64个字节。
分为三部分:
int编码: 元数据8B + INT 8b
long型整数: key 8 字节 + value 8个字节
dictEntry: key 8B + value 8B + next 8B
因为采用的是jemalloc的内存分配,jemalloc内存分配的时候会根据申请的字节数N,找一个比N大,但是最接近N的2次幂作为分配的空间,所以dictEntry最后是32位。
三部分加起来的总和是64位。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值