内射老阿姨1区2区3区4区_久久精品人人做人人爽电影蜜月_久久国产精品亚洲77777_99精品又大又爽又粗少妇毛片

探索Redis設(shè)計(jì)與實(shí)現(xiàn)8:連接底層與表面的數(shù)據(jù)結(jié)構(gòu)robj

本文轉(zhuǎn)自互聯(lián)網(wǎng)

創(chuàng)新互聯(lián)是一家專業(yè)提供錦州企業(yè)網(wǎng)站建設(shè),專注與網(wǎng)站設(shè)計(jì)、網(wǎng)站制作、成都h5網(wǎng)站建設(shè)、小程序制作等業(yè)務(wù)。10年已為錦州眾多企業(yè)、政府機(jī)構(gòu)等服務(wù)。創(chuàng)新互聯(lián)專業(yè)網(wǎng)站設(shè)計(jì)公司優(yōu)惠進(jìn)行中。

本系列文章將整理到我在GitHub上的《Java面試指南》倉庫,更多精彩內(nèi)容請到我的倉庫里查看

https://github.com/h3pl/Java-Tutorial

喜歡的話麻煩點(diǎn)下Star哈

文章首發(fā)于我的個(gè)人博客:

www.how2playlife.com

本文是微信公眾號【Java技術(shù)江湖】的《探索redis設(shè)計(jì)與實(shí)現(xiàn)》其中一篇,本文部分內(nèi)容來源于網(wǎng)絡(luò),為了把本文主題講得清晰透徹,也整合了很多我認(rèn)為不錯(cuò)的技術(shù)博客內(nèi)容,引用其中了一些比較好的博客文章,如有侵權(quán),請聯(lián)系作者。

該系列博文會(huì)告訴你如何從入門到進(jìn)階,Redis基本的使用方法,Redis的基本數(shù)據(jù)結(jié)構(gòu),以及一些進(jìn)階的使用方法,同時(shí)也需要進(jìn)一步了解Redis的底層數(shù)據(jù)結(jié)構(gòu),再接著,還會(huì)帶來Redis主從復(fù)制、集群、分布式鎖等方面的相關(guān)內(nèi)容,以及作為緩存的一些使用方法和注意事項(xiàng),以便讓你更完整地了解整個(gè)Redis相關(guān)的技術(shù)體系,形成自己的知識框架。

如果對本系列文章有什么建議,或者是有什么疑問的話,也可以關(guān)注公眾號【Java技術(shù)江湖】聯(lián)系作者,歡迎你參與本系列博文的創(chuàng)作和修訂。

本文是《 Redis內(nèi)部數(shù)據(jù)結(jié)構(gòu)詳解》系列的第三篇,講述在Redis實(shí)現(xiàn)中的一個(gè)基礎(chǔ)數(shù)據(jù)結(jié)構(gòu):robj。

那到底什么是robj呢?它有什么用呢?

從Redis的使用者的角度來看,一個(gè)Redis節(jié)點(diǎn)包含多個(gè)database(非cluster模式下默認(rèn)是16個(gè),cluster模式下只能是1個(gè)),而一個(gè)database維護(hù)了從key space到object space的映射關(guān)系。這個(gè)映射關(guān)系的key是string類型,而value可以是多種數(shù)據(jù)類型,比如:string, list, hash等。我們可以看到,key的類型固定是string,而value可能的類型是多個(gè)。

而從Redis內(nèi)部實(shí)現(xiàn)的角度來看,在前面第一篇文章中,我們已經(jīng)提到過,一個(gè)database內(nèi)的這個(gè)映射關(guān)系是用一個(gè)dict來維護(hù)的。dict的key固定用一種數(shù)據(jù)結(jié)構(gòu)來表達(dá)就夠了,這就是動(dòng)態(tài)字符串sds。而value則比較復(fù)雜,為了在同一個(gè)dict內(nèi)能夠存儲(chǔ)不同類型的value,這就需要一個(gè)通用的數(shù)據(jù)結(jié)構(gòu),這個(gè)通用的數(shù)據(jù)結(jié)構(gòu)就是robj(全名是redisObject)。舉個(gè)例子:如果value是一個(gè)list,那么它的內(nèi)部存儲(chǔ)結(jié)構(gòu)是一個(gè)quicklist(quicklist的具體實(shí)現(xiàn)我們放在后面的文章討論);如果value是一個(gè)string,那么它的內(nèi)部存儲(chǔ)結(jié)構(gòu)一般情況下是一個(gè)sds。當(dāng)然實(shí)際情況更復(fù)雜一點(diǎn),比如一個(gè)string類型的value,如果它的值是一個(gè)數(shù)字,那么Redis內(nèi)部還會(huì)把它轉(zhuǎn)成long型來存儲(chǔ),從而減小內(nèi)存使用。而一個(gè)robj既能表示一個(gè)sds,也能表示一個(gè)quicklist,甚至還能表示一個(gè)long型。

robj的數(shù)據(jù)結(jié)構(gòu)定義

在server.h中我們找到跟robj定義相關(guān)的代碼,如下(注意,本系列文章中的代碼片段全部來源于Redis源碼的3.2分支):

/* Object types */
#define OBJ_STRING 0
#define OBJ_LIST 1
#define OBJ_SET 2
#define OBJ_ZSET 3
#define OBJ_HASH 4
/* Objects encoding. Some kind of objects like Strings and Hashes can be
 * internally represented in multiple ways. The 'encoding' field of the object
 * is set to one of this fields for this object. */
#define OBJ_ENCODING_RAW 0     /* Raw representation */
#define OBJ_ENCODING_INT 1     /* Encoded as integer */
#define OBJ_ENCODING_HT 2      /* Encoded as hash table */
#define OBJ_ENCODING_ZIPMAP 3  /* Encoded as zipmap */
#define OBJ_ENCODING_LINKEDLIST 4 /* Encoded as regular linked list */
#define OBJ_ENCODING_ZIPLIST 5 /* Encoded as ziplist */
#define OBJ_ENCODING_INTSET 6  /* Encoded as intset */
#define OBJ_ENCODING_SKIPLIST 7  /* Encoded as skiplist */
#define OBJ_ENCODING_EMBSTR 8  /* Embedded sds string encoding */
#define OBJ_ENCODING_QUICKLIST 9 /* Encoded as linked list of ziplists */
#define LRU_BITS 24
typedef struct redisObject {
    unsigned type:4;
    unsigned encoding:4;
    unsigned lru:LRU_BITS; /* lru time (relative to server.lruclock) */
    int refcount;
    void *ptr;
} robj;

一個(gè)robj包含如下5個(gè)字段:

  • type: 對象的數(shù)據(jù)類型。占4個(gè)bit??赡艿娜≈涤?種:OBJ_STRING, OBJ_LIST, OBJ_SET, OBJ_ZSET, OBJ_HASH,分別對應(yīng)Redis對外暴露的5種數(shù)據(jù)結(jié)構(gòu)(即我們在第一篇文章中提到的第一個(gè)層面的5種數(shù)據(jù)結(jié)構(gòu))。
  • encoding: 對象的內(nèi)部表示方式(也可以稱為編碼)。占4個(gè)bit??赡艿娜≈涤?0種,即前面代碼中的10個(gè)OBJ_ENCODING_XXX常量。
  • lru: 做LRU替換算法用,占24個(gè)bit。這個(gè)不是我們這里討論的重點(diǎn),暫時(shí)忽略。
  • refcount: 引用計(jì)數(shù)。它允許robj對象在某些情況下被共享。
  • ptr: 數(shù)據(jù)指針。指向真正的數(shù)據(jù)。比如,一個(gè)代表string的robj,它的ptr可能指向一個(gè)sds結(jié)構(gòu);一個(gè)代表list的robj,它的ptr可能指向一個(gè)quicklist。

這里特別需要仔細(xì)察看的是encoding字段。對于同一個(gè)type,還可能對應(yīng)不同的encoding,這說明同樣的一個(gè)數(shù)據(jù)類型,可能存在不同的內(nèi)部表示方式。而不同的內(nèi)部表示,在內(nèi)存占用和查找性能上會(huì)有所不同。

比如,當(dāng)type = OBJ_STRING的時(shí)候,表示這個(gè)robj存儲(chǔ)的是一個(gè)string,這時(shí)encoding可以是下面3種中的一種:

  • OBJ_ENCODING_RAW: string采用原生的表示方式,即用sds來表示。
  • OBJ_ENCODING_INT: string采用數(shù)字的表示方式,實(shí)際上是一個(gè)long型。
  • OBJ_ENCODING_EMBSTR: string采用一種特殊的嵌入式的sds來表示。接下來我們會(huì)討論到這個(gè)細(xì)節(jié)。

再舉一個(gè)例子:當(dāng)type = OBJ_HASH的時(shí)候,表示這個(gè)robj存儲(chǔ)的是一個(gè)hash,這時(shí)encoding可以是下面2種中的一種:

  • OBJ_ENCODING_HT: hash采用一個(gè)dict來表示。
  • OBJ_ENCODING_ZIPLIST: hash采用一個(gè)ziplist來表示(ziplist的具體實(shí)現(xiàn)我們放在后面的文章討論)。

本文剩余主要部分將針對表示string的robj對象,圍繞它的3種不同的encoding來深入討論。前面代碼段中出現(xiàn)的所有10種encoding,在這里我們先簡單解釋一下,在這個(gè)系列后面的文章中,我們應(yīng)該還有機(jī)會(huì)碰到它們。

  • OBJ_ENCODING_RAW: 最原生的表示方式。其實(shí)只有string類型才會(huì)用這個(gè)encoding值(表示成sds)。
  • OBJ_ENCODING_INT: 表示成數(shù)字。實(shí)際用long表示。
  • OBJ_ENCODING_HT: 表示成dict。
  • OBJ_ENCODING_ZIPMAP: 是個(gè)舊的表示方式,已不再用。在小于Redis 2.6的版本中才有。
  • OBJ_ENCODING_LINKEDLIST: 也是個(gè)舊的表示方式,已不再用。
  • OBJ_ENCODING_ZIPLIST: 表示成ziplist。
  • OBJ_ENCODING_INTSET: 表示成intset。用于set數(shù)據(jù)結(jié)構(gòu)。
  • OBJ_ENCODING_SKIPLIST: 表示成skiplist。用于sorted set數(shù)據(jù)結(jié)構(gòu)。
  • OBJ_ENCODING_EMBSTR: 表示成一種特殊的嵌入式的sds。
  • OBJ_ENCODING_QUICKLIST: 表示成quicklist。用于list數(shù)據(jù)結(jié)構(gòu)。

我們來總結(jié)一下robj的作用:

  • 為多種數(shù)據(jù)類型提供一種統(tǒng)一的表示方式。
  • 允許同一類型的數(shù)據(jù)采用不同的內(nèi)部表示,從而在某些情況下盡量節(jié)省內(nèi)存。
  • 支持對象共享和引用計(jì)數(shù)。當(dāng)對象被共享的時(shí)候,只占用一份內(nèi)存拷貝,進(jìn)一步節(jié)省內(nèi)存。
string robj的編碼過程

當(dāng)我們執(zhí)行Redis的set命令的時(shí)候,Redis首先將接收到的value值(string類型)表示成一個(gè)type = OBJ_STRING并且encoding = OBJ_ENCODING_RAW的robj對象,然后在存入內(nèi)部存儲(chǔ)之前先執(zhí)行一個(gè)編碼過程,試圖將它表示成另一種更節(jié)省內(nèi)存的encoding方式。這一過程的核心代碼,是object.c中的tryObjectEncoding函數(shù)。

robj *tryObjectEncoding(robj *o) {
    long value;
    sds s = o->ptr;
    size_t len;
    /* Make sure this is a string object, the only type we encode
     * in this function. Other types use encoded memory efficient
     * representations but are handled by the commands implementing
     * the type. */
    serverAssertWithInfo(NULL,o,o->type == OBJ_STRING);
    /* We try some specialized encoding only for objects that are
     * RAW or EMBSTR encoded, in other words objects that are still
     * in represented by an actually array of chars. */
    if (!sdsEncodedObject(o)) return o;
    /* It's not safe to encode shared objects: shared objects can be shared
     * everywhere in the "object space" of Redis and may end in places where
     * they are not handled. We handle them only as values in the keyspace. */
     if (o->refcount > 1) return o;
    /* Check if we can represent this string as a long integer.
     * Note that we are sure that a string larger than 21 chars is not
     * representable as a 32 nor 64 bit integer. */
    len = sdslen(s);
    if (len <= 21 && string2l(s,len,&value)) {
        /* This object is encodable as a long. Try to use a shared object.
         * Note that we avoid using shared integers when maxmemory is used
         * because every object needs to have a private LRU field for the LRU
         * algorithm to work well. */
        if ((server.maxmemory == 0 ||
             (server.maxmemory_policy != MAXMEMORY_VOLATILE_LRU &&
              server.maxmemory_policy != MAXMEMORY_ALLKEYS_LRU)) &&
            value >= 0 &&
            value < OBJ_SHARED_INTEGERS)
        {
            decrRefCount(o);
            incrRefCount(shared.integers[value]);
            return shared.integers[value];
        } else {
            if (o->encoding == OBJ_ENCODING_RAW) sdsfree(o->ptr);
            o->encoding = OBJ_ENCODING_INT;
            o->ptr = (void*) value;
            return o;
        }
    }
    /* If the string is small and is still RAW encoded,
     * try the EMBSTR encoding which is more efficient.
     * In this representation the object and the SDS string are allocated
     * in the same chunk of memory to save space and cache misses. */
    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;
    }
    /* 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
     * OBJ_ENCODING_EMBSTR_SIZE_LIMIT. */
    if (o->encoding == OBJ_ENCODING_RAW &&
        sdsavail(s) > len/10)
    {
        o->ptr = sdsRemoveFreeSpace(o->ptr);
    }
    /* Return the original object. */
    return o;
}

這段代碼執(zhí)行的操作比較復(fù)雜,我們有必要仔細(xì)看一下每一步的操作:

  • 第1步檢查,檢查type。確保只對string類型的對象進(jìn)行操作。
  • 第2步檢查,檢查encoding。sdsEncodedObject是定義在server.h中的一個(gè)宏,確保只對OBJ_ENCODING_RAW和OBJ_ENCODING_EMBSTR編碼的string對象進(jìn)行操作。這兩種編碼的string都采用sds來存儲(chǔ),可以嘗試進(jìn)一步編碼處理。
#define sdsEncodedObject(objptr) (objptr->encoding == OBJ_ENCODING_RAW || objptr->encoding == OBJ_ENCODING_EMBSTR)
  • 第3步檢查,檢查refcount。引用計(jì)數(shù)大于1的共享對象,在多處被引用。由于編碼過程結(jié)束后robj的對象指針可能會(huì)變化(我們在前一篇介紹sdscatlen函數(shù)的時(shí)候提到過類似這種接口使用模式),這樣對于引用計(jì)數(shù)大于1的對象,就需要更新所有地方的引用,這不容易做到。因此,對于計(jì)數(shù)大于1的對象不做編碼處理。
  • 試圖將字符串轉(zhuǎn)成64位的long。64位的long所能表達(dá)的數(shù)據(jù)范圍是-2^63到2^63-1,用十進(jìn)制表達(dá)出來最長是20位數(shù)(包括負(fù)號)。這里判斷小于等于21,似乎是寫多了,實(shí)際判斷小于等于20就夠了(如果我算錯(cuò)了請一定告訴我哦)。string2l如果將字符串轉(zhuǎn)成long轉(zhuǎn)成功了,那么會(huì)返回1并且將轉(zhuǎn)好的long存到value變量里。
  • 在轉(zhuǎn)成long成功時(shí),又分為兩種情況。
    • 第一種情況:如果Redis的配置不要求運(yùn)行LRU替換算法,且轉(zhuǎn)成的long型數(shù)字的值又比較小(小于OBJ_SHARED_INTEGERS,在目前的實(shí)現(xiàn)中這個(gè)值是10000),那么會(huì)使用共享數(shù)字對象來表示。之所以這里的判斷跟LRU有關(guān),是因?yàn)長RU算法要求每個(gè)robj有不同的lru字段值,所以用了LRU就不能共享robj。shared.integers是一個(gè)長度為10000的數(shù)組,里面預(yù)存了10000個(gè)小的數(shù)字對象。這些小數(shù)字對象都是encoding = OBJ_ENCODING_INT的string robj對象。
    • 第二種情況:如果前一步不能使用共享小對象來表示,那么將原來的robj編碼成encoding = OBJ_ENCODING_INT,這時(shí)ptr字段直接存成這個(gè)long型的值。注意ptr字段本來是一個(gè)void *指針(即存儲(chǔ)的是內(nèi)存地址),因此在64位機(jī)器上有64位寬度,正好能存儲(chǔ)一個(gè)64位的long型值。這樣,除了robj本身之外,它就不再需要額外的內(nèi)存空間來存儲(chǔ)字符串值。
  • 接下來是對于那些不能轉(zhuǎn)成64位long的字符串進(jìn)行處理。最后再做兩步處理:
    • 如果字符串長度足夠?。ㄐ∮诘扔贠BJ_ENCODING_EMBSTR_SIZE_LIMIT,定義為44),那么調(diào)用createEmbeddedStringObject編碼成encoding = OBJ_ENCODING_EMBSTR;
    • 如果前面所有的編碼嘗試都沒有成功(仍然是OBJ_ENCODING_RAW),且sds里空余字節(jié)過多,那么做最后一次努力,調(diào)用sds的sdsRemoveFreeSpace接口來釋放空余字節(jié)。

其中調(diào)用的createEmbeddedStringObject,我們有必要看一下它的代碼:

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;
    o->lru = LRU_CLOCK();
    sh->len = len;
    sh->alloc = len;
    sh->flags = SDS_TYPE_8;
    if (ptr) {
        memcpy(sh->buf,ptr,len);
        sh->buf[len] = '\0';
    } else {
        memset(sh->buf,0,len+1);
    }
    return o;
}

createEmbeddedStringObject對sds重新分配內(nèi)存,將robj和sds放在一個(gè)連續(xù)的內(nèi)存塊中分配,這樣對于短字符串的存儲(chǔ)有利于減少內(nèi)存碎片。這個(gè)連續(xù)的內(nèi)存塊包含如下幾部分:

  • 16個(gè)字節(jié)的robj結(jié)構(gòu)。
  • 3個(gè)字節(jié)的sdshdr8頭。
  • 最多44個(gè)字節(jié)的sds字符數(shù)組。
  • 1個(gè)NULL結(jié)束符。

加起來一共不超過64字節(jié)(16+3+44+1),因此這樣的一個(gè)短字符串可以完全分配在一個(gè)64字節(jié)長度的內(nèi)存塊中。

string robj的解碼過程

當(dāng)我們需要獲取字符串的值,比如執(zhí)行g(shù)et命令的時(shí)候,我們需要執(zhí)行與前面講的編碼過程相反的操作——解碼。

這一解碼過程的核心代碼,是object.c中的getDecodedObject函數(shù)。

robj *getDecodedObject(robj *o) {
    robj *dec;
    if (sdsEncodedObject(o)) {
        incrRefCount(o);
        return o;
    }
    if (o->type == OBJ_STRING && o->encoding == OBJ_ENCODING_INT) {
        char buf[32];
        ll2string(buf,32,(long)o->ptr);
        dec = createStringObject(buf,strlen(buf));
        return dec;
    } else {
        serverPanic("Unknown encoding type");
    }
}

這個(gè)過程比較簡單,需要我們注意的點(diǎn)有:

  • 編碼為OBJ_ENCODING_RAW和OBJ_ENCODING_EMBSTR的字符串robj對象,不做變化,原封不動(dòng)返回。站在使用者的角度,這兩種編碼沒有什么區(qū)別,內(nèi)部都是封裝的sds。
  • 編碼為數(shù)字的字符串robj對象,將long重新轉(zhuǎn)為十進(jìn)制字符串的形式,然后調(diào)用createStringObject轉(zhuǎn)為sds的表示。注意:這里由long轉(zhuǎn)成的sds字符串長度肯定不超過20,而根據(jù)createStringObject的實(shí)現(xiàn),它們肯定會(huì)被編碼成OBJ_ENCODING_EMBSTR的對象。createStringObject的代碼如下:

    robj createStringObject(const char ptr, size_t len) {

    if (len <= OBJ_ENCODING_EMBSTR_SIZE_LIMIT)
        return createEmbeddedStringObject(ptr,len);
    else
        return createRawStringObject(ptr,len);
    

    }

再談sds與string的關(guān)系

在上一篇文章中,我們簡單地提到了sds與string的關(guān)系;在本文介紹了robj的概念之后,我們重新總結(jié)一下sds與string的關(guān)系。

  • 確切地說,string在Redis中是用一個(gè)robj來表示的。
  • 用來表示string的robj可能編碼成3種內(nèi)部表示:OBJ_ENCODING_RAW, OBJ_ENCODING_EMBSTR, OBJ_ENCODING_INT。其中前兩種編碼使用的是sds來存儲(chǔ),最后一種OBJ_ENCODING_INT編碼直接把string存成了long型。
  • 在對string進(jìn)行incr, decr等操作的時(shí)候,如果它內(nèi)部是OBJ_ENCODING_INT編碼,那么可以直接進(jìn)行加減操作;如果它內(nèi)部是OBJ_ENCODING_RAW或OBJ_ENCODING_EMBSTR編碼,那么Redis會(huì)先試圖把sds存儲(chǔ)的字符串轉(zhuǎn)成long型,如果能轉(zhuǎn)成功,再進(jìn)行加減操作。
  • 對一個(gè)內(nèi)部表示成long型的string執(zhí)行append, setbit, getrange這些命令,針對的仍然是string的值(即十進(jìn)制表示的字符串),而不是針對內(nèi)部表示的long型進(jìn)行操作。比如字符串”32”,如果按照字符數(shù)組來解釋,它包含兩個(gè)字符,它們的ASCII碼分別是0x33和0x32。當(dāng)我們執(zhí)行命令setbit key 7 0的時(shí)候,相當(dāng)于把字符0x33變成了0x32,這樣字符串的值就變成了”22”。而如果將字符串”32”按照內(nèi)部的64位long型來解釋,那么它是0x0000000000000020,在這個(gè)基礎(chǔ)上執(zhí)行setbit位操作,結(jié)果就完全不對了。因此,在這些命令的實(shí)現(xiàn)中,會(huì)把long型先轉(zhuǎn)成字符串再進(jìn)行相應(yīng)的操作。由于篇幅原因,這三個(gè)命令的實(shí)現(xiàn)代碼這里就不詳細(xì)介紹了,有興趣的讀者可以參考Redis源碼:
    • t_string.c中的appendCommand函數(shù);
    • biops.c中的setbitCommand函數(shù);
    • t_string.c中的getrangeCommand函數(shù)。

值得一提的是,append和setbit命令的實(shí)現(xiàn)中,都會(huì)最終調(diào)用到db.c中的dbUnshareStringValue函數(shù),將string對象的內(nèi)部編碼轉(zhuǎn)成OBJ_ENCODING_RAW的(只有這種編碼的robj對象,其內(nèi)部的sds 才能在后面自由追加新的內(nèi)容),并解除可能存在的對象共享狀態(tài)。這里面調(diào)用了前面提到的getDecodedObject。

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;
}
robj的引用計(jì)數(shù)操作

將robj的引用計(jì)數(shù)加1和減1的操作,定義在object.c中:

void incrRefCount(robj *o) {
    o->refcount++;
}
void decrRefCount(robj *o) {
    if (o->refcount <= 0) serverPanic("decrRefCount against refcount <= 0");
    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;
        default: serverPanic("Unknown object type"); break;
        }
        zfree(o);
    } else {
        o->refcount--;
    }
}

我們特別關(guān)注一下將引用計(jì)數(shù)減1的操作decrRefCount。如果只剩下最后一個(gè)引用了(refcount已經(jīng)是1了),那么在decrRefCount被調(diào)用后,整個(gè)robj將被釋放。

注意:Redis的del命令就依賴decrRefCount操作將value釋放掉。


經(jīng)過了本文的討論,我們很容易看出,robj所表示的就是Redis對外暴露的第一層面的數(shù)據(jù)結(jié)構(gòu):string, list, hash, set, sorted set,而每一種數(shù)據(jù)結(jié)構(gòu)的底層實(shí)現(xiàn)所對應(yīng)的是哪個(gè)(或哪些)第二層面的數(shù)據(jù)結(jié)構(gòu)(dict, sds, ziplist, quicklist, skiplist, 等),則通過不同的encoding來區(qū)分??梢哉f,robj是聯(lián)結(jié)兩個(gè)層面的數(shù)據(jù)結(jié)構(gòu)的橋梁。

本文詳細(xì)介紹了OBJ_STRING類型的字符串對象的底層實(shí)現(xiàn),其編碼和解碼過程在Redis里非常重要,應(yīng)用廣泛,我們在后面的討論中可能還會(huì)遇到。現(xiàn)在有了robj的概念基礎(chǔ),我們下一篇會(huì)討論ziplist,以及它與hash的關(guān)系。


后記(追加于2016-07-09): 本文在解析“將string編碼成long型”的代碼時(shí)提到的判斷21字節(jié)的問題,后來已經(jīng)提交給 @antirez并合并進(jìn)了unstable分支,詳見 commit f648c5a。

網(wǎng)站名稱:探索Redis設(shè)計(jì)與實(shí)現(xiàn)8:連接底層與表面的數(shù)據(jù)結(jié)構(gòu)robj
文章URL:http://www.rwnh.cn/article40/ipcseo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供手機(jī)網(wǎng)站建設(shè)網(wǎng)站維護(hù)、網(wǎng)站內(nèi)鏈做網(wǎng)站、Google、云服務(wù)器

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

網(wǎng)站建設(shè)網(wǎng)站維護(hù)公司
伊金霍洛旗| 宁晋县| 汉阴县| 即墨市| 丰台区| 金溪县| 阜新市| 株洲市| 巧家县| 夏邑县| 卢龙县| 泗洪县| 丽江市| 洛浦县| 大名县| 讷河市| 榆林市| 巩义市| 泾源县| 金平| 商城县| 汽车| 冕宁县| 阜新市| 宾川县| 阜康市| 贺兰县| 霍州市| 遂溪县| 双柏县| 滁州市| 布拖县| 溧阳市| 灵宝市| 望都县| 荔波县| 澜沧| 德保县| 清涧县| 滦平县| 台山市|