redis資料結構原始碼閱讀——字串編碼過程

江江蔣發表於2020-11-17

redis資料結構原始碼閱讀——字串編碼過程

對於RAW型別的字串,需要進一步進行編碼,判斷它是不是可以表示為整型數;更進一步地,看它能否成為共享變數。當然這都是為了節約記憶體。

/* Try to encode a string object in order to save space */
robj *tryObjectEncoding(robj *o) {
    long value;
    sds s = o->ptr;
    size_t len;

1. 判斷是不是能編碼

    /* 確保這是一個字串物件,
    * 這是我們在這個函式中編碼的唯一型別。
    * 其他型別使用編碼的高效記憶體表示,但由實現該型別的命令處理。 */
    // 判斷是不是字串物件
    redisAssertWithInfo(NULL,o,o->type == REDIS_STRING);

    // 判斷它的編碼型別
    if (!sdsEncodedObject(o)) return o;

    // 判斷它是不是共享物件,引用數是不是大於 1
    if (o->refcount > 1) return o;

2. 判斷是不是能編碼為INT

    /* 檢查是否可以把這個字串表示成一個長整數。
    * 請注意,大於21字元的字串是不可表示為32位或64位整數的。 */
    len = sdslen(s);
    // string2l: string 物件轉 long 物件的函式
    if (len <= 21 && string2l(s,len,&value)) {
        /* 此物件可編碼為long物件。嘗試使用共享物件。
        * 注意,當使用 maxmemory 時,我們避免使用共享整數,
        * 因為每個物件都需要有一個私有的LRU欄位,以便LRU演算法正常工作。 */
        if ((server.maxmemory == 0 ||
            (server.maxmemory_policy != REDIS_MAXMEMORY_VOLATILE_LRU &&
            server.maxmemory_policy != REDIS_MAXMEMORY_ALLKEYS_LRU)) &&
            value >= 0 &&
            value < REDIS_SHARED_INTEGERS)
        {
            // 釋放原來的 string 物件
            decrRefCount(o);
            // 新增新的全域性 long 型別引用
            incrRefCount(shared.integers[value]);
            return shared.integers[value];
        } else {
            // 把物件型別從 string 轉為 int
            if (o->encoding == REDIS_ENCODING_RAW) sdsfree(o->ptr);
            o->encoding = REDIS_ENCODING_INT;
            o->ptr = (void*) value;
            return o;
        }
    }

3. 判斷是不是能編碼為EMBSTR

    /* 如果字串很小,仍然是原始編碼,
    * 那麼嘗試EMBSTR編碼,它更有效。
    * 在這種表示中,物件和SDS字串在相同的記憶體塊中分配,
    * 以節省空間和快取丟失。 */
    if (len <= REDIS_ENCODING_EMBSTR_SIZE_LIMIT) {
        robj *emb;

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

4. 沒法編碼釋放其他空間

    /* We can't encode the object...
    *
    * Do the last try, and at least optimize the SDS string inside
    * the string object to require little space, in case there
    * is more than 10% of free space at the end of the SDS string.
    *
    * We do that only for relatively large strings as this branch
    * is only entered if the length of the string is greater than
    * REDIS_ENCODING_EMBSTR_SIZE_LIMIT. */
    if (o->encoding == REDIS_ENCODING_RAW &&
        sdsavail(s) > len/10)
    {
        o->ptr = sdsRemoveFreeSpace(o->ptr);
    }

    /* Return the original object. */
    return o;
}

相關文章