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

...

原文地址:https://www.cnblogs.com/ginkgo-leaf/p/8819591.html