zoukankan      html  css  js  c++  java
  • redis-ziplist

    ziplist, redis内部定义的双链表, 可实现t_hash, t_zset对象。


    ziplist数据结构: 总长度(uint32_t) + 尾结点偏移量(uint32_t) + 结点数(uint16_t) + 键结点 + 值结点 + 键结点 + 值结点 + 结点等.... + 键结点 + 尾结点 + END(255).

    结点数据结构:prevlensize(pre_encoding+前一个结点长度的字节数) + lensize(encodeing+len的字节数) + len(实际数据的字节数), 实现正向或反向查询

    结点数一般 <= UINT16_MAX - 1, 否则只能循环所有结点,获得结点数.
        
    查询时间复杂度 = o(n)
    t_hash.c 查询

    /* Get the value from a ziplist encoded hash, identified by field.
     * Returns -1 when the field cannot be found. */
    int hashTypeGetFromZiplist(robj *o, robj *field,
                               unsigned char **vstr,
                               unsigned int *vlen,
                               long long *vll)
    {
        unsigned char *zl, *fptr = NULL, *vptr = NULL;
        int ret;
    
        serverAssert(o->encoding == OBJ_ENCODING_ZIPLIST);
    
        field = getDecodedObject(field);
    
        zl = o->ptr;
        fptr = ziplistIndex(zl, ZIPLIST_HEAD);      //从首结点查询
        if (fptr != NULL) {
            fptr = ziplistFind(fptr, field->ptr, sdslen(field->ptr), 1);    //找到键结点
            if (fptr != NULL) {
                /* Grab pointer to the value (fptr points to the field) */
                vptr = ziplistNext(zl, fptr);           //找到值结点
                serverAssert(vptr != NULL);
            }
        }
    
        decrRefCount(field);
    
        if (vptr != NULL) {
            ret = ziplistGet(vptr, vstr, vlen, vll);    //通过值结点,获取相关数据(vstr或vll)
            serverAssert(ret);
            return 0;
        }
    
        return -1;
    }

     
    ziplist.c实现ziplistFind(), 获取键对应的指针

    /* Find pointer to the entry equal to the specified entry. Skip 'skip' entries
     * between every comparison. Returns NULL when the field could not be found. */
    unsigned char *ziplistFind(unsigned char *p, unsigned char *vstr, unsigned int vlen, unsigned int skip) {
        int skipcnt = 0;
        unsigned char vencoding = 0;
        long long vll = 0;
    
        while (p[0] != ZIP_END) {
            unsigned int prevlensize, encoding, lensize, len;
            unsigned char *q;
    
            ZIP_DECODE_PREVLENSIZE(p, prevlensize);
            ZIP_DECODE_LENGTH(p + prevlensize, encoding, lensize, len);
            q = p + prevlensize + lensize;            //实际数据指针
    
            if (skipcnt == 0) {
                /* Compare current entry with specified entry */
                if (ZIP_IS_STR(encoding)) {
                    if (len == vlen && memcmp(q, vstr, vlen) == 0) {    //如果是字符串,则字符比较
                        return p;
                    }
                } else {
                    /* Find out if the searched field can be encoded. Note that
                     * we do it only the first time, once done vencoding is set
                     * to non-zero and vll is set to the integer value. */
                    if (vencoding == 0) {
                        if (!zipTryEncoding(vstr, vlen, &vll, &vencoding)) {
                            /* If the entry can't be encoded we set it to
                             * UCHAR_MAX so that we don't retry again the next
                             * time. */
                            vencoding = UCHAR_MAX;
                        }
                        /* Must be non-zero by now */
                        assert(vencoding);
                    }
    
                    /* Compare current entry with specified entry, do it only
                     * if vencoding != UCHAR_MAX because if there is no encoding
                     * possible for the field it can't be a valid integer. */
                    if (vencoding != UCHAR_MAX) {
                        long long ll = zipLoadInteger(q, encoding);
                        if (ll == vll) {              //数值比较
                            return p;
                        }
                    }
                }
    
                /* Reset skip count */
                skipcnt = skip;
            } else {
                /* Skip entry */
                skipcnt--;
            }
    
            /* Move to next entry */
            p = q + len;
        }
    
        return NULL;
    }


    t_zset.c 查询

    unsigned char *zzlFind(unsigned char *zl, robj *ele, double *score) {
        unsigned char *eptr = ziplistIndex(zl,0), *sptr;    //eptr: 头结点
    
        ele = getDecodedObject(ele);
        while (eptr != NULL) {                //循环查询
            sptr = ziplistNext(zl,eptr);                //数值对应的结点
            serverAssertWithInfo(NULL,ele,sptr != NULL);
    
            if (ziplistCompare(eptr,ele->ptr,sdslen(ele->ptr))) {    //比较键
                /* Matching element, pull out score. */
                if (score != NULL) *score = zzlGetScore(sptr);      //获取积分数值
                decrRefCount(ele);
                return eptr;                         //返回键结点指针
            }
    
            /* Move to next element. */
            eptr = ziplistNext(zl,sptr);               //下一个键结点
        }
    
        decrRefCount(ele);
        return NULL;
    }


    t_hash.c 删除, 时间复杂度 = o(n) + o(m),               //n = 结点数, m = 被删除的结点后续结点数(需要调整结构)

    /* Delete an element from a hash.
     * Return 1 on deleted and 0 on not found. */
    int hashTypeDelete(robj *o, robj *field) {
        int deleted = 0;
    
        if (o->encoding == OBJ_ENCODING_ZIPLIST) {
            unsigned char *zl, *fptr;
    
            field = getDecodedObject(field);
    
            zl = o->ptr;                  //ziplist
            fptr = ziplistIndex(zl, ZIPLIST_HEAD);     //头结点
            if (fptr != NULL) {
                fptr = ziplistFind(fptr, field->ptr, sdslen(field->ptr), 1);    //找到field对应的键结点
                if (fptr != NULL) {
                    zl = ziplistDelete(zl,&fptr);      //删除键结点
                    zl = ziplistDelete(zl,&fptr);      //删除值结点
                    o->ptr = zl;
                    deleted = 1;
                }
            }
    
            decrRefCount(field);
    
        } else if (o->encoding == OBJ_ENCODING_HT) {
            if (dictDelete((dict*)o->ptr, field) == C_OK) {
                deleted = 1;
    
                /* Always check if the dictionary needs a resize after a delete. */
                if (htNeedsResize(o->ptr)) dictResize(o->ptr);
            }
    
        } else {
            serverPanic("Unknown hash encoding");
        }
    
        return deleted;
    }
    
    /* Delete a single entry from the ziplist, pointed to by *p.
     * Also update *p in place, to be able to iterate over the
     * ziplist, while deleting entries. */
    unsigned char *ziplistDelete(unsigned char *zl, unsigned char **p) {
        size_t offset = *p-zl;
        zl = __ziplistDelete(zl,*p,1);                //p结点后续结点调整结构(如果需要)
    
        /* Store pointer to current element in p, because ziplistDelete will
         * do a realloc which might result in a different "zl"-pointer.
         * When the delete direction is back to front, we might delete the last
         * entry and end up with "p" pointing to ZIP_END, so check this. */
        *p = zl+offset;
        return zl;
    }

    ...

  • 相关阅读:
    Yii2框架bootstrap样式理解
    spring 配置bean的方法及依赖注入发方式
    C#深拷贝
    【麦子学院】Linux cmd命令大全
    JEECG中的validform验证ajaxurl的使用方法
    ORACLE 如何查询被锁定表及如何解锁释放session
    获取请求真实ip
    jsp值传到后台Struts2中的action三种方法
    ajax 二级联动与springmvc 交互
    SpringMVC返回json数据的三种方式
  • 原文地址:https://www.cnblogs.com/ginkgo-leaf/p/8819591.html
Copyright © 2011-2022 走看看