Redis redisObject

作者: 烨哥 | 来源:发表于2018-03-20 20:52 被阅读17次

    简介

    redisObjet其实就是对应用类型的封装。简介面向对象的思想。实现对数据的统一管理保存

    数据结构

    typedef struct redisObject {
        unsigned type:4; //保存信息的类型
        unsigned encoding:4;//保存信息的编码方式
        unsigned lru:LRU_BITS; /* lru time (relative to server.lruclock) */
        int refcount;//引用次数
        void *ptr;//保存的指针
    } robj;
    /* 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 */
    

    这一块特别简单,type表示保存的类型。encode表示保存的方式。两个就可以确定我们的object;

    基本的方法

    robj *createObject(int type, void *ptr) {
        robj *o = zmalloc(sizeof(*o));//分配一个robj的空间
        o->type = 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;
    }
    
    robj *makeObjectShared(robj *o) {
        serverAssert(o->refcount == 1); //创建一个共享的对象
        o->refcount = OBJ_SHARED_REFCOUNT;
        return o;
    }
    
    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;//sds类型
        if (ptr) {//拷贝内存
            memcpy(sh->buf,ptr,len);
            sh->buf[len] = '\0';
        } else {
            memset(sh->buf,0,len+1);
        }
        return o;
    }
    
    
    /* Create a string object with EMBSTR encoding if it is smaller than
     * REIDS_ENCODING_EMBSTR_SIZE_LIMIT, otherwise the RAW encoding is
     * used.
     *
     * The current limit of 39 is chosen so that the biggest string object
     * we allocate as EMBSTR will still fit into the 64 byte arena of jemalloc. */
    #define OBJ_ENCODING_EMBSTR_SIZE_LIMIT 44
    robj *createStringObject(const char *ptr, size_t len) {
        //sizeof(redisObject) = 16 
        //sizeof(sdshdr8) = 3
        //一个 \0 =1
        //就只剩下44了
        if (len <= OBJ_ENCODING_EMBSTR_SIZE_LIMIT)
            return createEmbeddedStringObject(ptr,len);
        else
            return createRawStringObject(ptr,len);
    }
    

    引用的操作

    void incrRefCount(robj *o) {
        if (o->refcount != OBJ_SHARED_REFCOUNT) o->refcount++;//增加引用计数
    }
    
    void decrRefCount(robj *o) {//减少引用计数
        if (o->refcount == 1) {//引用计数为0释放对象
            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;
            case OBJ_MODULE: freeModuleObject(o); break;
            default: serverPanic("Unknown object type"); break;
            }
            zfree(o);
        } else {
            if (o->refcount <= 0) serverPanic("decrRefCount against refcount <= 0");
            if (o->refcount != OBJ_SHARED_REFCOUNT) o->refcount--;
        }
    }
    

    一些辅助操作

    /* Try to encode a string object in order to save space */
    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);//确定是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;//是不是编码的数据 排除掉int那些
    
        /* 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 20 chars is not
         * representable as a 32 nor 64 bit integer. */
        len = sdslen(s);//获取长度
        if (len <= 20 && 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_FLAG_NO_SHARED_INTEGERS)) &&
                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;//设置为int保存
                o->ptr = (void*) value;//使用指针的内存来保存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));//转换成emb
            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;
    }
    robj *getDecodedObject(robj *o) {
        robj *dec;
    
        if (sdsEncodedObject(o)) {//对于int保存的数据解码出来
            incrRefCount(o);
            return o;
        }
        if (o->type == OBJ_STRING && o->encoding == OBJ_ENCODING_INT) {
            char buf[32];
    
            ll2string(buf,32,(long)o->ptr);//转成string
            dec = createStringObject(buf,strlen(buf));
            return dec;
        } else {
            serverPanic("Unknown encoding type");
        }
    }
    
    #define REDIS_COMPARE_BINARY (1<<0)
    #define REDIS_COMPARE_COLL (1<<1)
    
    int compareStringObjectsWithFlags(robj *a, robj *b, int flags) {//string 不叫
        serverAssertWithInfo(NULL,a,a->type == OBJ_STRING && b->type == OBJ_STRING);//必须两个都是string
        char bufa[128], bufb[128], *astr, *bstr;
        size_t alen, blen, minlen;
    
        if (a == b) return 0;//内存地址一样 没啥说的
        if (sdsEncodedObject(a)) { 
            astr = a->ptr;
            alen = sdslen(astr);
        } else {
            alen = ll2string(bufa,sizeof(bufa),(long) a->ptr);//把使用数字保存的解码出来
            astr = bufa;
        }
        if (sdsEncodedObject(b)) {
            bstr = b->ptr;
            blen = sdslen(bstr);
        } else {
            blen = ll2string(bufb,sizeof(bufb),(long) b->ptr);
            bstr = bufb;
        }
        if (flags & REDIS_COMPARE_COLL) {
            return strcoll(astr,bstr);//使用strcoll比较
        } else {
            int cmp;
    
            minlen = (alen < blen) ? alen : blen;//最短的
            cmp = memcmp(astr,bstr,minlen);//内存比较
            if (cmp == 0) return alen-blen;
            return cmp;
        }
    }
    int equalStringObjects(robj *a, robj *b) {//
        if (a->encoding == OBJ_ENCODING_INT &&
            b->encoding == OBJ_ENCODING_INT){//对于两个都是数字保存的 直接比较值
            /* If both strings are integer encoded just check if the stored
             * long is the same. */
            return a->ptr == b->ptr;
        } else {
            return compareStringObjects(a,b) == 0;//否则使用内存比较
        }
    }
    
    /* Returns the size in bytes consumed by the key's value in RAM.
     * Note that the returned value is just an approximation, especially in the
     * case of aggregated data types where only "sample_size" elements
     * are checked and averaged to estimate the total size. */
    #define OBJ_COMPUTE_SIZE_DEF_SAMPLES 5 /* Default sample size. */
    size_t objectComputeSize(robj *o, size_t sample_size) {//这个玩意是返回个大概值 aggregated data types 他最多便利sample_size 个然后再来整个平均值
        sds ele, ele2;
        dict *d;
        dictIterator *di;
        struct dictEntry *de;
        size_t asize = 0, elesize = 0, samples = 0;
    
        if (o->type == OBJ_STRING) {
            if(o->encoding == OBJ_ENCODING_INT) {//对于值的保存没有使用额外的空间
                asize = sizeof(*o);
            } else if(o->encoding == OBJ_ENCODING_RAW) {//对于内存保存的
                asize = sdsAllocSize(o->ptr)+sizeof(*o);//加上额外的string空间
            } else if(o->encoding == OBJ_ENCODING_EMBSTR) {
                asize = sdslen(o->ptr)+2+sizeof(*o);//多余的两个头信息
            } else {
                serverPanic("Unknown string encoding");
            }
        } else if (o->type == OBJ_LIST) {
            if (o->encoding == OBJ_ENCODING_QUICKLIST) {
                quicklist *ql = o->ptr;
                quicklistNode *node = ql->head;
                asize = sizeof(*o)+sizeof(quicklist);//quicklist本身的长度 加上robjet的长度
                do {
                    elesize += sizeof(quicklistNode)+ziplistBlobLen(node->zl);//加上node本身的长度和ziplist的长度
                    samples++;
                } while ((node = node->next) && samples < sample_size);//不一定会跑完哦
                asize += (double)elesize/samples*ql->len;//取个估计值 反正就是没跑完
            } else if (o->encoding == OBJ_ENCODING_ZIPLIST) {
                asize = sizeof(*o)+ziplistBlobLen(o->ptr);//ziplist 直接拿
            } else {
                serverPanic("Unknown list encoding");
            }
        } else if (o->type == OBJ_SET) {//set
            if (o->encoding == OBJ_ENCODING_HT) {//是个hash
                d = o->ptr;
                di = dictGetIterator(d);//拉取遍历 
                asize = sizeof(*o)+sizeof(dict)+(sizeof(struct dictEntry*)*dictSlots(d));//robject dict dictEntry* *slots 分配的个数
                while((de = dictNext(di)) != NULL && samples < sample_size) {
                    ele = dictGetKey(de);//拉取key 不过我在setDictType 没看到keydup 不过有个dictSdsDestructor 不过具体的set没去看 
                    elesize += sizeof(struct dictEntry) + sdsAllocSize(ele);//
                    samples++;
                }
                dictReleaseIterator(di);
                if (samples) asize += (double)elesize/samples*dictSize(d);//来来继续算个大概值
            } else if (o->encoding == OBJ_ENCODING_INTSET) {//intset
                intset *is = o->ptr;
                asize = sizeof(*o)+sizeof(*is)+is->encoding*is->length;//直接干
            } else {
                serverPanic("Unknown set encoding");
            }
        } else if (o->type == OBJ_ZSET) {//zset
            if (o->encoding == OBJ_ENCODING_ZIPLIST) {
                asize = sizeof(*o)+(ziplistBlobLen(o->ptr));//ziplist
            } else if (o->encoding == OBJ_ENCODING_SKIPLIST) {
                d = ((zset*)o->ptr)->dict;
                zskiplist *zsl = ((zset*)o->ptr)->zsl;
                zskiplistNode *znode = zsl->header->level[0].forward;
                asize = sizeof(*o)+sizeof(zset)+(sizeof(struct dictEntry*)*dictSlots(d));//基本大小
                while(znode != NULL && samples < sample_size) {
                    elesize += sdsAllocSize(znode->ele);//保存对象的大小
                    elesize += sizeof(struct dictEntry) + zmalloc_size(znode);//zone的大小 因为是随机来的 对应一个dictentry
                    samples++;
                    znode = znode->level[0].forward;
                }
                if (samples) asize += (double)elesize/samples*dictSize(d);//大概值
            } else {
                serverPanic("Unknown sorted set encoding");
            }
        } else if (o->type == OBJ_HASH) {
            if (o->encoding == OBJ_ENCODING_ZIPLIST) {
                asize = sizeof(*o)+(ziplistBlobLen(o->ptr));
            } else if (o->encoding == OBJ_ENCODING_HT) {
                d = o->ptr;
                di = dictGetIterator(d);
                asize = sizeof(*o)+sizeof(dict)+(sizeof(struct dictEntry*)*dictSlots(d));
                while((de = dictNext(di)) != NULL && samples < sample_size) {
                    ele = dictGetKey(de); //hash就有了两个方法
                    ele2 = dictGetVal(de);
                    elesize += sdsAllocSize(ele) + sdsAllocSize(ele2);
                    elesize += sizeof(struct dictEntry);
                    samples++;
                }
                dictReleaseIterator(di);
                if (samples) asize += (double)elesize/samples*dictSize(d);
            } else {
                serverPanic("Unknown hash encoding");
            }
        } else if (o->type == OBJ_MODULE) {
            moduleValue *mv = o->ptr;
            moduleType *mt = mv->type;
            if (mt->mem_usage != NULL) {
                asize = mt->mem_usage(mv->value);
            } else {
                asize = 0;
            }
        } else {
            serverPanic("Unknown object type");
        }
        return asize;
    }
    

    这里的话没有什么新东西。随意看下就好。

    相关文章

      网友评论

        本文标题:Redis redisObject

        本文链接:https://www.haomeiwen.com/subject/axlbqftx.html