redis6.0.5之zset阅读笔记5--zset公共排序集相关API

***********************************************************************************************
获取zset的元素个数
unsigned long zsetLength(const robj *zobj) {
    unsigned long length = 0;
    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {  如果是压表编码方式
        length = zzlLength(zobj->ptr);  调用获取ziplist的元素个数函数
    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) { 如果是跳表编码方式
        length = ((const zset*)zobj->ptr)->zsl->length;  获取跳表的元素个数
    } else {
        serverPanic("Unknown sorted set encoding"); 
    }
    return length;
}
***********************************************************************************************
zset编码转化

typedef struct redisObject {
    unsigned type:4;     //类型
    unsigned encoding:4;  //编码方式,就是存储方式
    unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or
                            * LFU data (least significant 8 bits frequency
                            * and most significant 16 bits access time). */
    //这个地段需要根据不同的淘汰算法来决定,如果使用LRU,那么就是一个时间值(和全局lru_clock相关)
    //或者LFU数据(最低8位表示频率同时最高16位表示存取时间)
    int refcount;  //引用次数,次数为了表示没有引用,可以释放
    void *ptr;   //指向对象保存的数据
} robj;

/* Sorted sets hash (note: a skiplist is used in addition to the hash table) */
排序集hash(注意: 在hash表的基础上额外使用一个跳表)
dictType zsetDictType = {
    dictSdsHash,               /* hash function */
    NULL,                      /* key dup */
    NULL,                      /* val dup */
    dictSdsKeyCompare,         /* key compare */
    NULL,                      /* Note: SDS string shared & freed by skiplist */
    NULL                       /* val destructor */
};

void zsetConvert(robj *zobj, int encoding) {
    zset *zs;
    zskiplistNode *node, *next;
    sds ele;
    double score;

    if (zobj->encoding == encoding) return;  如果当前编码和传入编码一致,不用转化
    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) { 如果原编码为压表   压表->跳表
        unsigned char *zl = zobj->ptr;
        unsigned char *eptr, *sptr;
        unsigned char *vstr;
        unsigned int vlen;
        long long vlong;

        if (encoding != OBJ_ENCODING_SKIPLIST)目标不为跳表,不知道转什么编码
            serverPanic("Unknown target encoding");

        zs = zmalloc(sizeof(*zs)); 分配zset空间
        zs->dict = dictCreate(&zsetDictType,NULL); 创建字典
        zs->zsl = zslCreate(); 创建跳表

        eptr = ziplistIndex(zl,0); 指向压缩链表的头实体
        serverAssertWithInfo(NULL,zobj,eptr != NULL);
        sptr = ziplistNext(zl,eptr);获取第二个实体即第一个数值
        serverAssertWithInfo(NULL,zobj,sptr != NULL);

        while (eptr != NULL) {  还存在实体对
            score = zzlGetScore(sptr); 获取数值
            serverAssertWithInfo(NULL,zobj,ziplistGet(eptr,&vstr,&vlen,&vlong)); 获取字符串
            if (vstr == NULL)  
                字符串以数字形式保存
                ele = sdsfromlonglong(vlong); 从数字转化为字符串,
            else
                ele = sdsnewlen((char*)vstr,vlen);  根据字符串创建SDS字符串

            node = zslInsert(zs->zsl,score,ele);跳表插入一个节点
            serverAssert(dictAdd(zs->dict,ele,&node->score) == DICT_OK);字典添加一个key-value对
            zzlNext(zl,&eptr,&sptr); 获取下一对 字符串和数值 实体
        }

        zfree(zobj->ptr); 释放保存在对象中的数据
        zobj->ptr = zs; 指向新创建的跳表
        zobj->encoding = OBJ_ENCODING_SKIPLIST; 修改对象编码方式
    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {  跳表->压表
        unsigned char *zl = ziplistNew(); 创建新压表

        if (encoding != OBJ_ENCODING_ZIPLIST) 目标编码不为压表, 不知道转什么编码方式
            serverPanic("Unknown target encoding");

        /* Approach similar to zslFree(), since we want to free the skiplist at
         * the same time as creating the ziplist. */
方法类似函数zslFree,因为我们想要在穿件压表的同时释放跳表
        zs = zobj->ptr; zset集合
        dictRelease(zs->dict); 释放字典
        node = zs->zsl->header->level[0].forward; 指向跳表第一个数据节点
        zfree(zs->zsl->header);释放头节点
        zfree(zs->zsl); 释放跳表

        while (node) { 还存在节点
            zl = zzlInsertAt(zl,NULL,node->ele,node->score);从尾部插入压表(全部从尾部插入)
            next = node->level[0].forward; 指向下一个节点
            zslFreeNode(node); 释放本节点
            node = next;
        }

        zfree(zs); 释放zset
        zobj->ptr = zl; 指向压表
        zobj->encoding = OBJ_ENCODING_ZIPLIST; 修改编码方式
    } else {
        serverPanic("Unknown sorted set encoding");
    }
}
***********************************************************************************************

/* Convert the sorted set object into a ziplist if it is not already a ziplist
 * and if the number of elements and the maximum element size is within the
 * expected ranges. */
如果元素的数量和最大元素的大小在预期范围内,并且原本不是压表,那么转化排序集对象为压表
void zsetConvertToZiplistIfNeeded(robj *zobj, size_t maxelelen) {
    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) return; 是压表,不需要操作
    zset *zset = zobj->ptr;
#createSizeTConfig("zset-max-ziplist-entries", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.zset_max_ziplist_entries, 128, INTEGER_CONFIG, NULL, NULL),
#createSizeTConfig("zset-max-ziplist-value", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.zset_max_ziplist_value, 64, MEMORY_CONFIG, NULL, NULL),

    if (zset->zsl->length <= server.zset_max_ziplist_entries &&  个数小于128
        maxelelen <= server.zset_max_ziplist_value)   最大元素长度小于64
            zsetConvert(zobj,OBJ_ENCODING_ZIPLIST);  将排序集转化为压表
}
***********************************************************************************************
/* Return (by reference) the score of the specified member of the sorted set
 * storing it into *score. If the element does not exist C_ERR is returned
 * otherwise C_OK is returned and *score is correctly populated.
 * If 'zobj' or 'member' is NULL, C_ERR is returned. */
返回(通过引用)排序集的特定成员的数值,保存在*score中。如果元素不存在,那么返回C_ERR,
否则返回C_OK并且正确填写*score。如果'zobj' 活着 'member' 为空,则返回C_ERR
int zsetScore(robj *zobj, sds member, double *score) {
    if (!zobj || !member) return C_ERR; 排序集或者待查找元素为空,返回错误

    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) { 排序集用的是压表
        if (zzlFind(zobj->ptr, member, score) == NULL) return C_ERR; 
    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) { 如果用的是跳表
        zset *zs = zobj->ptr;
        dictEntry *de = dictFind(zs->dict, member); 获取元素对应的实体
        if (de == NULL) return C_ERR;
        *score = *(double*)dictGetVal(de); 获取数值
    } else {
        serverPanic("Unknown sorted set encoding");
    }
    return C_OK;
}
***********************************************************************************************
/* Add a new element or update the score of an existing element in a sorted
 * set, regardless of its encoding.
在排序集中,新增一个元素或者更新已经存在元素的数值,不管排序集用的哪种编码方式
 * The set of flags change the command behavior. They are passed with an integer
 * pointer since the function will clear the flags and populate them with
 * other flags to indicate different conditions.
标志集改变命令的行为(就是命令根据标志不同而不同)。他们通过一个整型指针传入,
函数会清除标志再填写另外的标志来表示不同的状态
 * The input flags are the following:
输入的标志集如下:
 * ZADD_INCR: Increment the current element score by 'score' instead of updating
 *            the current element score. If the element does not exist, we
 *            assume 0 as previous score.
ZADD_INCR:通过score增加当前元素的数值 代替 更新当前元素的数值。如果元素不存在,我们假设0就是前数值
 * ZADD_NX:   Perform the operation only if the element does not exist.
ZADD_NX:   执行这个操作 仅当 元素不存在
 * ZADD_XX:   Perform the operation only if the element already exist.
ZADD_XX: 执行这个操作 仅当 元素已经存在
 * When ZADD_INCR is used, the new score of the element is stored in
 * '*newscore' if 'newscore' is not NULL.
当使用ZADD_INCR时,元素新的数值被保存在*newscore,如果*newscore非空

 * The returned flags are the following:
返回的标志集如下:
 * ZADD_NAN:     The resulting score is not a number.
ZADD_NAN:   结果数值不是一个数字
 * ZADD_ADDED:   The element was added (not present before the call).
ZADD_ADDED: 元素被添加(在本次调用前不存在)
 * ZADD_UPDATED: The element score was updated.
ZADD_UPDATED: 元素的数值被更新
 * ZADD_NOP:     No operation was performed because of NX or XX.
ZADD_NOP: 没有操作被执行 因为 NX 或者 XX

 * Return value: 返回值
 *
 * The function returns 1 on success, and sets the appropriate flags
 * ADDED or UPDATED to signal what happened during the operation (note that
 * none could be set if we re-added an element using the same score it used
 * to have, or in the case a zero increment is used).
成功函数返回1同时设置合适的标志集ADDED 或者 UPDATED 来表示操作过程发生了什么
(注意如果我们使用同样的数值重新添加一个元素或者在一个使用了加0的操作,那么标志集什么也不会设置)
 * The function returns 0 on error, currently only when the increment
 * produces a NAN condition, or when the 'score' value is NAN since the
 * start.
错误函数返回0,当前只有在增加产生NAN的情况,或者 一开始数值的值是NAN
 * The command as a side effect of adding a new element may convert the sorted
 * set internal encoding from ziplist to hashtable+skiplist.
这个命令的一个副作用是 添加一个新的元素可能导致排序集内部的编码从压表向跳表+hash表转变
 * Memory management of 'ele':
关于ele的内存管理
 * The function does not take ownership of the 'ele' SDS string, but copies
 * it if needed. */
本函数不拥有自己的sds字符串,只有需要的时候拷贝下使用

/* Input flags. */
#define ZADD_NONE 0
#define ZADD_INCR (1<<0)    /* Increment the score instead of setting it. */
#define ZADD_NX (1<<1)      /* Don't touch elements not already existing. */
#define ZADD_XX (1<<2)      /* Only touch elements already existing. */

/* Output flags. */
#define ZADD_NOP (1<<3)     /* Operation not performed because of conditionals.*/
#define ZADD_NAN (1<<4)     /* Only touch elements already existing. */
#define ZADD_ADDED (1<<5)   /* The element was new and was added. */
#define ZADD_UPDATED (1<<6) /* The element already existed, score updated. */

int zsetAdd(robj *zobj, double score, sds ele, int *flags, double *newscore) {
    /* Turn options into simple to check vars. */ 转化选择项 为简单的检查变量
    int incr = (*flags & ZADD_INCR) != 0;
    int nx = (*flags & ZADD_NX) != 0;
    int xx = (*flags & ZADD_XX) != 0;
    *flags = 0; /* We'll return our response flags. */ 清除传入标志,用来返回内部执行状态
    double curscore;

    /* NaN as input is an error regardless of all the other parameters. */
不用考虑其它任何参数,输入NaN就是一个错误,返回失败
    if (isnan(score)) {
        *flags = ZADD_NAN;
        return 0;
    }

    /* Update the sorted set according to its encoding. */根据编码方式更新排序集
    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) { 压表
        unsigned char *eptr;

        if ((eptr = zzlFind(zobj->ptr,ele,&curscore)) != NULL) {
            /* NX? Return, same element already exists. */ 同样的元素已经存在,NX情况下,什么也不用做,返回成功
            if (nx) {
                *flags |= ZADD_NOP;
                return 1;
            }

            /* Prepare the score for the increment if needed. */ 为增量 准备好数值
            if (incr) {
                score += curscore; 新值
                if (isnan(score)) {  是NaN,只是返回失败
                    *flags |= ZADD_NAN;
                    return 0;
                }
                if (newscore) *newscore = score; 
            }

            /* Remove and re-insert when score changed. */ 当数值改变时候,需要先删除后插入
            if (score != curscore) {
                zobj->ptr = zzlDelete(zobj->ptr,eptr);
                zobj->ptr = zzlInsert(zobj->ptr,ele,score);
                *flags |= ZADD_UPDATED; 设置更新标志
            }
            return 1;
        } else if (!xx) {  压表中没有找到对应元素 并且 操作符号为非 XX(需要元素存在)
            /* Optimize: check if the element is too large or the list
             * becomes too long *before* executing zzlInsert. */
             优化,在执行插入元素之前 检查元素是否过大 或者链表变成太长,显然作者这里还么有优化
            zobj->ptr = zzlInsert(zobj->ptr,ele,score);插入新实体对
            if (zzlLength(zobj->ptr) > server.zset_max_ziplist_entries || 超过最大实体数
                sdslen(ele) > server.zset_max_ziplist_value)  超过最大元素长度
                zsetConvert(zobj,OBJ_ENCODING_SKIPLIST);
            if (newscore) *newscore = score;  填写新值
            *flags |= ZADD_ADDED;
            return 1;
        } else {
            *flags |= ZADD_NOP;
            return 1;
        }
    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) { 跳表
        zset *zs = zobj->ptr;
        zskiplistNode *znode;
        dictEntry *de;

        de = dictFind(zs->dict,ele); 在字典中查找对应元素
        if (de != NULL) {
            /* NX? Return, same element already exists. */ 已经存在同样的元素,并且是NX 什么也不用做,直接返回成功
            if (nx) {
                *flags |= ZADD_NOP;
                return 1;
            }
            curscore = *(double*)dictGetVal(de); 获取原来的数值

            /* Prepare the score for the increment if needed. */
            if (incr) {  如果是增加,就加上外面传入的数值
                score += curscore;
                if (isnan(score)) {
                    *flags |= ZADD_NAN;
                    return 0;
                }
                if (newscore) *newscore = score;
            }

            /* Remove and re-insert when score changes. */ 数值变了,需要删除和重新插入节点
            if (score != curscore) {
                znode = zslUpdateScore(zs->zsl,curscore,ele,score);
                /* Note that we did not removed the original element from
                 * the hash table representing the sorted set, so we just
                 * update the score. */
                 注意到我们没有从排序集的hash表中删除原来的元素,所以我们只需要更新下即可
                dictGetVal(de) = &znode->score; /* Update score ptr. */ 更新数值指针,指向新数值
                *flags |= ZADD_UPDATED;
            }
            return 1;
        } else if (!xx) {  插入新节点
            ele = sdsdup(ele); 拷贝字符串
            znode = zslInsert(zs->zsl,score,ele); 插入新节点
            serverAssert(dictAdd(zs->dict,ele,&znode->score) == DICT_OK); 同时也添加到hash表中
            *flags |= ZADD_ADDED;
            if (newscore) *newscore = score;
            return 1;
        } else {
            *flags |= ZADD_NOP;
            return 1;
        }
    } else {
        serverPanic("Unknown sorted set encoding");
    }
    return 0; /* Never reached. */
}

***********************************************************************************************
从排序集中删除元素ele,返回1如果元素存在并且被删除,否则返回0(元素不存在)
/* Delete the element 'ele' from the sorted set, returning 1 if the element
 * existed and was deleted, 0 otherwise (the element was not there). */
int zsetDel(robj *zobj, sds ele) {
    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) { 压表
        unsigned char *eptr;

        if ((eptr = zzlFind(zobj->ptr,ele,NULL)) != NULL) {
            zobj->ptr = zzlDelete(zobj->ptr,eptr); 存在删除即可
            return 1;
        }
    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) { 跳表
        zset *zs = zobj->ptr;
        dictEntry *de;
        double score;

        de = dictUnlink(zs->dict,ele); 删除元素,但是不释放,返回删除的元素
        if (de != NULL) {
            /* Get the score in order to delete from the skiplist later. */
            获取数值为了后面从跳表中删除节点
            score = *(double*)dictGetVal(de);

            /* Delete from the hash table and later from the skiplist.
             * Note that the order is important: deleting from the skiplist
             * actually releases the SDS string representing the element,
             * which is shared between the skiplist and the hash table, so
             * we need to delete from the skiplist as the final step. */
             首先删除hash表中的元素,然后才从跳表中删除。
             注意这个顺序是重要的: 因为从跳表删除节点会释放代表节点的sds字符串,
             这个sds字符串是由跳表和hash表共享的,所以我们需要把从跳表中删除节点放在最后一步
             
            dictFreeUnlinkedEntry(zs->dict,de); 这里才是真的释放hash表中的这个节点

            /* Delete from skiplist. */ 最后从跳表删除节点
            int retval = zslDelete(zs->zsl,score,ele,NULL);
            serverAssert(retval);

            if (htNeedsResize(zs->dict)) dictResize(zs->dict); hash表是否需要迁移
            return 1;
        }
    } else {
        serverPanic("Unknown sorted set encoding");
    }
    return 0; /* No such element found. */
}
***********************************************************************************************
/* Given a sorted set object returns the 0-based rank of the object or
 * -1 if the object does not exist.
给定一个排序集对象,返回对象基于0的秩或者-1如果对象不存在。
 * For rank we mean the position of the element in the sorted collection
 * of elements. So the first element has rank 0, the second rank 1, and so
 * forth up to length-1 elements.
秩在我们这里的意思是 元素在排序集所有元素中的位置。所以第一个元素的秩是0,第二个的秩是1,直到length-1个元素
 * If 'reverse' is false, the rank is returned considering as first element
 * the one with the lowest score. Otherwise if 'reverse' is non-zero
 * the rank is computed considering as element with rank 0 the one with
 * the highest score. */
如果反向是假的,那么秩 具有最小数值的元素被人为是第一个元素的秩,
否则,反向是非0,那么 具有最大数值的元素被人为是第一个元素的秩,且秩为0。
long zsetRank(robj *zobj, sds ele, int reverse) {
    unsigned long llen;
    unsigned long rank;

    llen = zsetLength(zobj); 获取排序集元素个数

    if (zobj->encoding == OBJ_ENCODING_ZIPLIST) { 压表
        unsigned char *zl = zobj->ptr;
        unsigned char *eptr, *sptr;

        eptr = ziplistIndex(zl,0);
        serverAssert(eptr != NULL);
        sptr = ziplistNext(zl,eptr);
        serverAssert(sptr != NULL);

        rank = 1;
        while(eptr != NULL) {
            if (ziplistCompare(eptr,(unsigned char*)ele,sdslen(ele)))
                break;
            rank++;
            zzlNext(zl,&eptr,&sptr);
        }

        if (eptr != NULL) {
            if (reverse) 反向为真,从大到小
                return llen-rank;
            else 从小到大
                return rank-1; 需要减去1
        } else {
            return -1;  找不到
        }
    } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) { 跳表
        zset *zs = zobj->ptr;
        zskiplist *zsl = zs->zsl;
        dictEntry *de;
        double score;

        de = dictFind(zs->dict,ele); 字典查找元素
        if (de != NULL) {
            score = *(double*)dictGetVal(de);
            rank = zslGetRank(zsl,score,ele); 获取秩
            /* Existing elements always have a rank. */ 存在的元素总是有秩的
            serverAssert(rank != 0);
            if (reverse)
                return llen-rank;
            else
                return rank-1;
        } else {
            return -1;
        }
    } else {
        serverPanic("Unknown sorted set encoding");
    }
}
***********************************************************************************************

 

posted on 2021-02-09 17:40  子虚乌有  阅读(204)  评论(0)    收藏  举报