ICode9

精准搜索请尝试: 精确搜索
首页 > 数据库> 文章详细

redis6.0.5之zset阅读笔记4--压缩列表(ziplist)排序集相关API

2021-02-08 17:37:12  阅读:238  来源: 互联网

标签:zl char redis6.0 zset -- eptr unsigned sptr NULL


***********************************************************************************************
获取压缩链表中节点的数值
double zzlGetScore(unsigned char *sptr) {
    unsigned char *vstr;
    unsigned int vlen;
    long long vlong;
    char buf[128];
    double score;

    serverAssert(sptr != NULL);
    serverAssert(ziplistGet(sptr,&vstr,&vlen,&vlong));

    if (vstr) {  如果是字符串编码,需要转化为数值
        memcpy(buf,vstr,vlen);
        buf[vlen] = '\0';
        score = strtod(buf,NULL);
    } else {
        score = vlong;  是数值就直接赋值
    }

    return score;
}
***********************************************************************************************
/* Return a ziplist element as an SDS string. */
以SDS字符串形式返回一个压缩链表的元素
sds ziplistGetObject(unsigned char *sptr) {
    unsigned char *vstr;
    unsigned int vlen;
    long long vlong;

    serverAssert(sptr != NULL);
    serverAssert(ziplistGet(sptr,&vstr,&vlen,&vlong));

    if (vstr) {
        return sdsnewlen((char*)vstr,vlen);  新建字符串
    } else {
        return sdsfromlonglong(vlong); 将数值转化为字符串
    }
}
***********************************************************************************************
/* Compare element in sorted set with given element. */
有序集合中的元素和给定的元素 进行比较
int zzlCompareElements(unsigned char *eptr, unsigned char *cstr, unsigned int clen) {
    unsigned char *vstr;
    unsigned int vlen;
    long long vlong;
    unsigned char vbuf[32];
    int minlen, cmp;

    serverAssert(ziplistGet(eptr,&vstr,&vlen,&vlong));
    if (vstr == NULL) {
        /* Store string representation of long long in buf. */
        vlen = ll2string((char*)vbuf,sizeof(vbuf),vlong);  转为字符串
        vstr = vbuf;
    }

    minlen = (vlen < clen) ? vlen : clen; 取两个字符短的长度
    cmp = memcmp(vstr,cstr,minlen);比较字符串
    if (cmp == 0) return vlen-clen; 如果在同样长度的情况下一样,那么只要谁长就是谁大
    return cmp;
}
***********************************************************************************************
unsigned int zzlLength(unsigned char *zl) {
    return ziplistLen(zl)/2;  因为需要用两个实体来表示一个元素  数值  和  字符串 ,所以需要除以2
}
***********************************************************************************************
/* Move to next entry based on the values in eptr and sptr. Both are set to
 * NULL when there is no next entry. */
基于指针eptr 和 sptr指向的内容 移动到下个实体。没有下个实体,eptr 和 sptr都指向空
void zzlNext(unsigned char *zl, unsigned char **eptr, unsigned char **sptr) {
    unsigned char *_eptr, *_sptr;
    serverAssert(*eptr != NULL && *sptr != NULL);

    _eptr = ziplistNext(zl,*sptr);      获取字符串
    if (_eptr != NULL) {
        _sptr = ziplistNext(zl,_eptr);  获取score,即数值
        serverAssert(_sptr != NULL);
    } else {
        /* No next entry. */ 没有下个实体了
        _sptr = NULL;
    }

    *eptr = _eptr; 给指针指向的内容赋值
    *sptr = _sptr;
}
***********************************************************************************************
/* Move to the previous entry based on the values in eptr and sptr. Both are
 * set to NULL when there is no next entry. */
基于指针eptr 和 sptr指向的内容 移动到前一个实体。没有前一个实体,eptr 和 sptr都指向空
void zzlPrev(unsigned char *zl, unsigned char **eptr, unsigned char **sptr) {
    unsigned char *_eptr, *_sptr;
    serverAssert(*eptr != NULL && *sptr != NULL);

    _sptr = ziplistPrev(zl,*eptr);  获取score,即数值
    if (_sptr != NULL) {
        _eptr = ziplistPrev(zl,_sptr); 获取字符串
        serverAssert(_eptr != NULL);
    } else {
        /* No previous entry. */
        _eptr = NULL;
    }

    *eptr = _eptr;
    *sptr = _sptr;
}
***********************************************************************************************
/* Returns if there is a part of the zset is in range. Should only be used
 * internally by zzlFirstInRange and zzlLastInRange. */
返回zset的一部分是不是在给定区间中(和给定区间是否有交集)。
只能被函数zzlFirstInRange 和 zzlLastInRange 内部使用
int zzlIsInRange(unsigned char *zl, zrangespec *range) {
    unsigned char *p;
    double score;

    /* Test for ranges that will always be empty. */ 确认给定区间不为空
    if (range->min > range->max ||
            (range->min == range->max && (range->minex || range->maxex)))
        return 0;

    p = ziplistIndex(zl,-1); /* Last score. */  最后一个实体  里面是数值
    if (p == NULL) return 0; /* Empty sorted set */ 最后一个元素的空,那就是空集
    score = zzlGetScore(p);  获取具体的数值
    if (!zslValueGteMin(score,range))   是否大于区间的最小值
        return 0;

    p = ziplistIndex(zl,1); /* First score. */ 第一个数值实体
    serverAssert(p != NULL);
    score = zzlGetScore(p); 获取数值
    if (!zslValueLteMax(score,range))  最小值是否大于区间的最大值
        return 0;

    return 1;
}
***********************************************************************************************
/* Find pointer to the first element contained in the specified range.
 * Returns NULL when no element is contained in the range. */
找到指向  压缩链表 中 第一个在给定区间内 元素 的指针。 返回空如果没有元素在给定区间内。
unsigned char *zzlFirstInRange(unsigned char *zl, zrangespec *range) {
    unsigned char *eptr = ziplistIndex(zl,0), *sptr;
    double score;

    /* If everything is out of range, return early. */ 如果没有交集,尽早返回空
    if (!zzlIsInRange(zl,range)) return NULL;

    while (eptr != NULL) {
        sptr = ziplistNext(zl,eptr);  获取数值
        serverAssert(sptr != NULL);

        score = zzlGetScore(sptr);
        if (zslValueGteMin(score,range)) { 确认是否大于给定区间最小值
            /* Check if score <= max. */
            if (zslValueLteMax(score,range)) 确认是否大于给定区间最大值
                return eptr; 满足条件就返回
            return NULL;
        }

        /* Move to next element. */ 当小于给定区间最小值时,继续向下一个比较
        eptr = ziplistNext(zl,sptr); 跳过一个字符串
    }

    return NULL;
}
***********************************************************************************************
/* Find pointer to the last element contained in the specified range.
 * Returns NULL when no element is contained in the range. */
找到指向  压缩链表 中 最后一个在给定区间内 元素 的指针。 返回空如果没有元素在给定区间内。
unsigned char *zzlLastInRange(unsigned char *zl, zrangespec *range) {
    unsigned char *eptr = ziplistIndex(zl,-2), *sptr;
    double score;

    /* If everything is out of range, return early. */
    if (!zzlIsInRange(zl,range)) return NULL;

    while (eptr != NULL) {
        sptr = ziplistNext(zl,eptr);  获取下一个元素,指向数值的指针
        serverAssert(sptr != NULL);

        score = zzlGetScore(sptr);  获取数值
        if (zslValueLteMax(score,range)) {  如果压缩链表最后一个数值 小于给定区间最大值
            /* Check if score >= min. */
            if (zslValueGteMin(score,range))  同时满足大于 给定区间最小值
                return eptr; 那么就是我们要寻找的元素
            return NULL;
        }

        /* Move to previous element by moving to the score of previous element.
         * When this returns NULL, we know there also is no element. */
        sptr = ziplistPrev(zl,eptr); 回退到前面的 指向数值的指针
        if (sptr != NULL)
            serverAssert((eptr = ziplistPrev(zl,sptr)) != NULL);  
            回退到前面的指向字符串的指针。理论上是不能为空的,因为是成对出现的。
        else
            eptr = NULL;
    }

    return NULL;
}
***********************************************************************************************
int zslLexValueGteMin(sds value, zlexrangespec *spec) {
    return spec->minex ?
        (sdscmplex(value,spec->min) > 0) :
        (sdscmplex(value,spec->min) >= 0);
}

int zslLexValueLteMax(sds value, zlexrangespec *spec) {
    return spec->maxex ?
        (sdscmplex(value,spec->max) < 0) :
        (sdscmplex(value,spec->max) <= 0);
}

int zzlLexValueGteMin(unsigned char *p, zlexrangespec *spec) {
    sds value = ziplistGetObject(p);  返回压缩链表的值 
    int res = zslLexValueGteMin(value,spec); 是否大于给定区间的最小值
    sdsfree(value); 释放字符串,免得引起内存泄漏
    return res;
}

int zzlLexValueLteMax(unsigned char *p, zlexrangespec *spec) {
    sds value = ziplistGetObject(p);
    int res = zslLexValueLteMax(value,spec); 是否小于给定区间的最大值
    sdsfree(value);
    return res;
}
***********************************************************************************************
/* Returns if there is a part of the zset is in range. Should only be used
 * internally by zzlFirstInRange and zzlLastInRange. */
返回zset的一部分是不是在给定区间中(和给定区间是否有交集)。
只能被函数zzlFirstInLexRange 和 zzlLastInLexRange 内部使用(这里两个函数名称是作者的笔误)
int zzlIsInLexRange(unsigned char *zl, zlexrangespec *range) {
    unsigned char *p;

    /* Test for ranges that will always be empty. */  确认区间非空
    int cmp = sdscmplex(range->min,range->max);
    if (cmp > 0 || (cmp == 0 && (range->minex || range->maxex)))
        return 0;

    p = ziplistIndex(zl,-2); /* Last element. */ 最后一个sds字符串
    if (p == NULL) return 0;
    if (!zzlLexValueGteMin(p,range)) 字典序比较 压缩链表最大值 是否小于给定区间最小值
        return 0;

    p = ziplistIndex(zl,0); /* First element. */第一个sds字符串
    serverAssert(p != NULL);
    if (!zzlLexValueLteMax(p,range))字典序比较 压缩链表最小值 是否大于给定区间最大值
        return 0;

    return 1;
}
***********************************************************************************************
/* Find pointer to the first element contained in the specified lex range.
 * Returns NULL when no element is contained in the range. */
找到指向  压缩链表 中 第一个在给定字典序区间内 元素 的指针。 返回空如果没有元素在给定区间内。
unsigned char *zzlFirstInLexRange(unsigned char *zl, zlexrangespec *range) {
    unsigned char *eptr = ziplistIndex(zl,0), *sptr;

    /* If everything is out of range, return early. */ 没有交集,尽早返回空
    if (!zzlIsInLexRange(zl,range)) return NULL;

    while (eptr != NULL) {
        if (zzlLexValueGteMin(eptr,range)) {  是否大于给定字典序区间最小值
            /* Check if score <= max. */
            if (zzlLexValueLteMax(eptr,range))  如果同时满足小于给定字典序区间最大值,就满足条件了,直接返回
                return eptr;
            return NULL;
        }

        /* Move to next element. */ 小于给定字典序区间最小值,继续查找下一个
        sptr = ziplistNext(zl,eptr); /* This element score. Skip it. */ 跳过数值元素
        serverAssert(sptr != NULL); 理论上是不为空的,实际检测一下,防止出现bug
        eptr = ziplistNext(zl,sptr); /* Next element. */  下个元素
    }

    return NULL;
}
***********************************************************************************************
/* Find pointer to the last element contained in the specified lex range.
 * Returns NULL when no element is contained in the range. */
找到指向  压缩链表 中 最后一个在给定字典序区间内 元素 的指针。 返回空如果没有元素在给定区间内。
unsigned char *zzlLastInLexRange(unsigned char *zl, zlexrangespec *range) {
    unsigned char *eptr = ziplistIndex(zl,-2), *sptr;

    /* If everything is out of range, return early. */
    if (!zzlIsInLexRange(zl,range)) return NULL;

    while (eptr != NULL) {
        if (zzlLexValueLteMax(eptr,range)) {  最后一个元素 小于给定字典序区间最大值
            /* Check if score >= min. */
            if (zzlLexValueGteMin(eptr,range))  同时 大于给定字典序区间最小值,满足条件,直接返回
                return eptr;
            return NULL;
        }

        /* Move to previous element by moving to the score of previous element.
         * When this returns NULL, we know there also is no element. */
移动到前一个元素通过移动到前一个元素的数值部分。
当数值部分是空的,我们就知道前面已经没有元素了。
        sptr = ziplistPrev(zl,eptr);  移动到前一个的数值部分
        if (sptr != NULL) 不为空,继续移动到字符串部分
            serverAssert((eptr = ziplistPrev(zl,sptr)) != NULL);
        else
            eptr = NULL;
    }

    return NULL;
}
***********************************************************************************************
unsigned char *zzlFind(unsigned char *zl, sds ele, double *score) {
    unsigned char *eptr = ziplistIndex(zl,0), *sptr;

    while (eptr != NULL) {  从压缩链表头节点开始查找
        sptr = ziplistNext(zl,eptr);  获取数值
        serverAssert(sptr != NULL);

        if (ziplistCompare(eptr,(unsigned char*)ele,sdslen(ele))) {  比较sds字符串
            /* Matching element, pull out score. */ 匹配字符串,获取数值
            if (score != NULL) *score = zzlGetScore(sptr);
            return eptr;
        }

        /* Move to next element. */ 移动到下个字符串
        eptr = ziplistNext(zl,sptr);
    }
    return NULL;
}
***********************************************************************************************
/* Delete (element,score) pair from ziplist. Use local copy of eptr because we
 * don't want to modify the one given as argument. */
从压缩链表删除(元素,数值)对。使用指针eptr的本地变量,因为我们不希望修改传入的参数eptr
unsigned char *zzlDelete(unsigned char *zl, unsigned char *eptr) {
    unsigned char *p = eptr;

    /* TODO: add function to ziplist API to delete N elements from offset. */
    将来要做  增加从偏移量开始删除N个元素的函数
    zl = ziplistDelete(zl,&p);  删除字符串部分
    zl = ziplistDelete(zl,&p);  删除数值部分
    return zl;
}
***********************************************************************************************
unsigned char *zzlInsertAt(unsigned char *zl, unsigned char *eptr, sds ele, double score) {
    unsigned char *sptr;
    char scorebuf[128];
    int scorelen;
    size_t offset;

    scorelen = d2string(scorebuf,sizeof(scorebuf),score); 将double数值转化为字符串
    if (eptr == NULL) {
        zl = ziplistPush(zl,(unsigned char*)ele,sdslen(ele),ZIPLIST_TAIL); 在压缩链表尾部插入字符串实体
        zl = ziplistPush(zl,(unsigned char*)scorebuf,scorelen,ZIPLIST_TAIL); 在压缩链表尾部插入数值实体
    } else {
        /* Keep offset relative to zl, as it might be re-allocated. */ 记住压缩链表的偏移量,因为压缩链表可能被重新分配
        offset = eptr-zl;
        zl = ziplistInsert(zl,eptr,(unsigned char*)ele,sdslen(ele)); 在指针eptr指向的实体 插入新的字符串
        eptr = zl+offset;  定位到新插入实体的开始位置

        /* Insert score after the element. */ 在字符串后面插入数值实体
        serverAssert((sptr = ziplistNext(zl,eptr)) != NULL); 获取插入实体的位置
        zl = ziplistInsert(zl,sptr,(unsigned char*)scorebuf,scorelen);
    }
    return zl;
}
***********************************************************************************************
/* Insert (element,score) pair in ziplist. This function assumes the element is
 * not yet present in the list. */
在压缩链表中插入(字符串,数值)对。这个函数假设字符串没有在链表中出现过
unsigned char *zzlInsert(unsigned char *zl, sds ele, double score) {
    unsigned char *eptr = ziplistIndex(zl,0), *sptr;
    double s;

    while (eptr != NULL) {
        sptr = ziplistNext(zl,eptr); 获取数值指向指针
        serverAssert(sptr != NULL);
        s = zzlGetScore(sptr); 获取数值

        if (s > score) {  链表中实体的数值 大于 要插入实体的数值
            /* First element with score larger than score for element to be
             * inserted. This means we should take its spot in the list to
             * maintain ordering. */
             这个是第一个大于传入待插入数值的实体数值。
             意味着我们该在这个点插入新实体来保持链表顺序
            zl = zzlInsertAt(zl,eptr,ele,score); 插入字符串和数值
            break;
        } else if (s == score) { 相等的情况,需要比较字符串的大小
            /* Ensure lexicographical ordering for elements. */ 确认字符串的字典序大小
            if (zzlCompareElements(eptr,(unsigned char*)ele,sdslen(ele)) > 0) { 
            如果链表中的字符串比较大,那么就在这个位置插入新字符串
                zl = zzlInsertAt(zl,eptr,ele,score);
                break;
            }
        }

        /* Move to next element. */ 还是传入的参数大,继续下一个比较
        eptr = ziplistNext(zl,sptr);
    }

    /* Push on tail of list when it was not yet inserted. */ 
    如果没有找到合适的位置,意味传入参数 比链表中任何一个实体都要大,那么就添加在最后面
    if (eptr == NULL)
        zl = zzlInsertAt(zl,NULL,ele,score);
    return zl;
}
***********************************************************************************************
unsigned char *zzlDeleteRangeByScore(unsigned char *zl, zrangespec *range, unsigned long *deleted) {
    unsigned char *eptr, *sptr;
    double score;
    unsigned long num = 0;

    if (deleted != NULL) *deleted = 0;

    eptr = zzlFirstInRange(zl,range); 查找交集的第一个元素
    if (eptr == NULL) return zl; 

    /* When the tail of the ziplist is deleted, eptr will point to the sentinel
     * byte and ziplistNext will return NULL. */
     如果压缩链表的尾巴节点被删除,那么指针eptr就指向哨兵字节(即尾部字节FF),函数ziplistNext返回空
    while ((sptr = ziplistNext(zl,eptr)) != NULL) {
        score = zzlGetScore(sptr);
        if (zslValueLteMax(score,range)) { 数值小于给定区间的最大值
            /* Delete both the element and the score. */ 把字符串和数值两者全部删除
            zl = ziplistDelete(zl,&eptr);
            zl = ziplistDelete(zl,&eptr);
            num++;删除对数加1
        } else {
            /* No longer in range. */ 不在给定区间范围内,无需删除
            break;
        }
    }

    if (deleted != NULL) *deleted = num; 传入指针deleted非空,返回删除对数
    return zl;
}
***********************************************************************************************
删除给定字典序区间中的链表元素,分析完全同上个函数一样
unsigned char *zzlDeleteRangeByLex(unsigned char *zl, zlexrangespec *range, unsigned long *deleted) {
    unsigned char *eptr, *sptr;
    unsigned long num = 0;

    if (deleted != NULL) *deleted = 0;

    eptr = zzlFirstInLexRange(zl,range);  字典序 ,第一个交集元素
    if (eptr == NULL) return zl;

    /* When the tail of the ziplist is deleted, eptr will point to the sentinel
     * byte and ziplistNext will return NULL. */
     如果压缩链表的尾巴节点被删除,那么指针eptr就指向哨兵字节(即尾部字节FF),函数ziplistNext返回空
    while ((sptr = ziplistNext(zl,eptr)) != NULL) {
        if (zzlLexValueLteMax(eptr,range)) {  字典序 小于给定区间的最大值
            /* Delete both the element and the score. */
            zl = ziplistDelete(zl,&eptr);
            zl = ziplistDelete(zl,&eptr);
            num++;
        } else {
            /* No longer in range. */
            break;
        }
    }

    if (deleted != NULL) *deleted = num;
    return zl;
}
***********************************************************************************************
/* Delete all the elements with rank between start and end from the skiplist.这里笔误,应为ziplist
 * Start and end are inclusive. Note that start and end need to be 1-based */
删除压缩链表中所有位于从开始位置到结束位置的节点。
包括开始和结束.注意开始和结束位置都是基于1的(至少是1,并且都是1的整数倍)
unsigned char *zzlDeleteRangeByRank(unsigned char *zl, unsigned int start, unsigned int end, unsigned long *deleted) {
    unsigned int num = (end-start)+1;  删除的对数
    if (deleted) *deleted = num;
    zl = ziplistDeleteRange(zl,2*(start-1),2*num);  从2*(start-1) 开始, 后面连续删除num对,即2*num个实体
    return zl;
}
***********************************************************************************************

 

标签:zl,char,redis6.0,zset,--,eptr,unsigned,sptr,NULL
来源: https://www.cnblogs.com/cquccy/p/14389704.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有