Redis深度歷險 - embstr和raw的字符串

Redis深度離線 - embstr和raw的字符串

在Redis中字符串存儲有兩種方式,embstr和raw兩種形式,不超過44字節(jié)的情況下以embstr存儲,超過44字節(jié)則以raw形式存儲

image-002.png

\color{red}{注:Redis中底層保證字符串的存儲必然以0結尾,雖然是44個字符但是obj顯示確是45}


embstr vs raw

Redis字符串結構體

struct __attribute__ ((__packed__)) sdshdr5 {
    unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 {
    uint8_t len; /* used */
    uint8_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};
struct __attribute__ ((__packed__)) sdshdr16 {
    uint16_t len; /* used */
    uint16_t alloc; /* excluding the header and null terminator */
    unsigned char flags; /* 3 lsb of type, 5 unused bits */
    char buf[];
};

??Redis字符串的實現(xiàn)原理是在在字符串頭部額外分配空間存儲上述這樣一個結構體,使用變長結構體存儲長度、容量等信息;視長度而采取不通的結構體。

??字符串較短是SDS頭部最少占用3個字節(jié)(sdshdr5廢棄),不過在Redis中上述SDS字符串只是基礎數(shù)據(jù)結構并不由Redis直接使用

Redis對象結構體

typedef struct redisObject {
    unsigned type:4;
    unsigned encoding:4;
    unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or
                            * LFU data (least significant 8 bits frequency
                            * and most significant 16 bits access time). */
    int refcount;
    void *ptr;
} robj;

#define LRU_BITS 24

??所有Redis對象都有上述結構體,上述結構體長度為(4+4+24+4*8+8*8)/8即16字節(jié);分配一個字符串最少頭部就需要占用19個字節(jié)。

創(chuàng)建字符串對象

#define OBJ_ENCODING_EMBSTR_SIZE_LIMIT 44
robj *createStringObject(const char *ptr, size_t len) {
    if (len <= OBJ_ENCODING_EMBSTR_SIZE_LIMIT)
        return createEmbeddedStringObject(ptr,len);
    else
        return createRawStringObject(ptr,len);
}

??在創(chuàng)建字符串結構時通過宏定義根據(jù)長度區(qū)分創(chuàng)建的方式

/* Create a string object with encoding OBJ_ENCODING_RAW, that is a plain
 * string object where o->ptr points to a proper sds string. */
robj *createRawStringObject(const char *ptr, size_t len) {
    return createObject(OBJ_STRING, sdsnewlen(ptr,len));
}

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;
}

??創(chuàng)建SDS字符串然后將指針賦給RedisObject的ptr中。

/* Create a string object with encoding OBJ_ENCODING_EMBSTR, that is
 * an object where the sds string is actually an unmodifiable string
 * allocated in the same chunk as the object itself. */
robj *createEmbeddedStringObject(const char *ptr, size_t len) {
    robj *o = zmalloc(sizeof(robj)+sizeof(struct sdshdr8)+len+1);
    struct sdshdr8 *sh = (void*)(o+1);

    o->type = OBJ_STRING;
    o->encoding = OBJ_ENCODING_EMBSTR;
    o->ptr = sh+1;
    o->refcount = 1;
    if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {
        o->lru = (LFUGetTimeInMinutes()<<8) | LFU_INIT_VAL;
    } else {
        o->lru = LRU_CLOCK();
    }

    sh->len = len;
    sh->alloc = len;
    sh->flags = SDS_TYPE_8;
    if (ptr == SDS_NOINIT)
        sh->buf[len] = '\0';
    else if (ptr) {
        memcpy(sh->buf,ptr,len);
        sh->buf[len] = '\0';
    } else {
        memset(sh->buf,0,len+1);
    }
    return o;
}

??創(chuàng)建字符串對象時直接創(chuàng)建了RedisObj+sdsHeader+string的連續(xù)空間,即RedisOjbect的空間和SDS字符串是連在一起的

結構說明

image-001.png

為什么是44個字節(jié)?

??因為Redis中內存是統(tǒng)一控制分配的,通常是是2、4、8、16、32、64等;64-19-1=44就是原因。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容