redis 为什么对数字/字符串append操作后,编码格式object encoding从int/embstr变成raw了

Posted liuhmmjj

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了redis 为什么对数字/字符串append操作后,编码格式object encoding从int/embstr变成raw了相关的知识,希望对你有一定的参考价值。

不单单int编码类型(type是string),进行字符串操作后,会自动转码为raw;对普通的短字符串(长度小于等于44个字节) 进行append操作后,编码格式也会发生改变(即使操作后长度还是小于等于44个字节也会由原来的embstr变成raw)!

embstr存储形式将 RedisObject 对象头和 SDS 对象连续存在一起,使用 malloc 方法一次分配。

redis 3.2之后empstr只能容纳44字节:

 ​​​​embstr的最小占用空间为19(16+3),而64-19-1(结尾的\\0)=44,所以empstr只能容纳44字节。

但是当执行append命令之后,即使append之后的字符串长度小于等于44字节也会转化为raw

例如:

 append源码分析:

append命令执行后会进入到 src/t_string.c下的appendCommand方法
void appendCommand(client *c) 
    size_t totlen;
    robj *o, *append;

    o = lookupKeyWrite(c->db,c->argv[1]);
    if (o == NULL) 
        /* Create the key */
        c->argv[2] = tryObjectEncoding(c->argv[2]);
        dbAdd(c->db,c->argv[1],c->argv[2]);
        incrRefCount(c->argv[2]);
        totlen = stringObjectLen(c->argv[2]);
     else 
        /* Key exists, check type */
        if (checkType(c,o,OBJ_STRING))
            return;

        /* "append" is an argument, so always an sds */
        append = c->argv[2];
        totlen = stringObjectLen(o)+sdslen(append->ptr);
        if (checkStringLength(c,totlen) != C_OK)
            return;

        /* Append the value */
        o = dbUnshareStringValue(c->db,c->argv[1],o);
        o->ptr = sdscatlen(o->ptr,append->ptr,sdslen(append->ptr));
        totlen = sdslen(o->ptr);
    
    signalModifiedKey(c,c->db,c->argv[1]);
    notifyKeyspaceEvent(NOTIFY_STRING,"append",c->argv[1],c->db->id);
    server.dirty++;
    addReplyLongLong(c,totlen);
打断点会进入到上面的dbUnshareStringValue方法
robj *dbUnshareStringValue(redisDb *db, robj *key, robj *o) 
    serverAssert(o->type == OBJ_STRING);
    if (o->refcount != 1 || o->encoding != OBJ_ENCODING_RAW) 
        robj *decoded = getDecodedObject(o);
        o = createRawStringObject(decoded->ptr, sdslen(decoded->ptr));
        decrRefCount(decoded);
        dbOverwrite(db,key,o);
    
    return o;

然后重点关注一下这一行:

o = createRawStringObject(decoded->ptr, sdslen(decoded->ptr));
robj *createRawStringObject(const char *ptr, size_t len) 
    return createObject(OBJ_STRING, sdsnewlen(ptr,len));

接着进入到createObject方法

robj *createObject(int type, void *ptr) 
    robj *o = zmalloc(sizeof(*o));
    o->type = type;
    o->encoding = OBJ_ENCODING_RAW;
    o->ptr = ptr;
    o->refcount = 1;

    /* Set the LRU to the current lruclock (minutes resolution), or
     * alternatively the LFU counter. */
    if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) 
        o->lru = (LFUGetTimeInMinutes()<<8) | LFU_INIT_VAL;
     else 
        o->lru = LRU_CLOCK();
    
    return o;
从这一行o->encoding = OBJ_ENCODING_RAW;可以看到创建的新object的编码格式为raw类型。
总结:在对embstr对象修改时其实都是新创建了一个raw对象,然后对raw对象进行修改,因此,当执行append命令之后,即使append之后的字符串长度小于等于44字节也会转化为raw

以上是关于redis 为什么对数字/字符串append操作后,编码格式object encoding从int/embstr变成raw了的主要内容,如果未能解决你的问题,请参考以下文章

Redis Lua Scripting

redis 体系结构

Python操作Redis

redis学习

使用Redis之前5个必须了解的事情

Redis在C#中的使用及Redis的封装