精华内容
下载资源
问答
  • zset

    2019-10-27 13:50:29
    zset类型 sorted set,有序集合 元素为string类型 元素具有唯一性,不重复 每个元素都会关联一个double类型的score,标识权重,通过权重将元素从⼩到⼤排序 说明:没有修改操作 增加 添加 zadd key score1 member1 ...

    zset类型

    • sorted set,有序集合
    • 元素为string类型
    • 元素具有唯一性,不重复
    • 每个元素都会关联一个double类型的score,标识权重,通过权重将元素从⼩到⼤排序
    • 说明:没有修改操作

    增加

    • 添加

      zadd key score1 member1 score2 menber2 …

    • 例1:向键a4的集合中添加元素lisiwangwuzhaoliuzhangsan,权重分别为4、5、6、3

      zadd a4 4 lisi 5 wangwu 6 zhaoliu 3 zhangsan

    获取

    • 返回指定范围内的元素

    • start、stop为元素的下标索引

    • 索引从左侧开始,第⼀个元素为0

    • 索引可以是负数,表示从尾部开始计数,如-1表示最后⼀个元素

      zrange key start stop

    • 例2:获取键a4的集合中所有元素

      zrange a4 0 -1

      在这里插入图片描述

    • 返回score值在minmax之间的成员

      zrangebyscore key min max

    • 例3:获取键a4的集合中权限值在5和6之间的成员

      zrangebyscore a4 5 6

      在这里插入图片描述

    • 返回成员memberscore

      zscore key member

      在这里插入图片描述

    删除

    • 删除指定元素

      zrem key member1 member2 …

    • 例5:删除集合a4中元素zhangsan

      zrem a4 zhangsan

      在这里插入图片描述

    • 删除权重在指定范围的元素

      zremrangebyscore key min max

    • 例6:删除集合a4中权限在5、6之间的元素

      zremrangebyscore a4 5 6

      在这里插入图片描述

    展开全文
  • typedef struct zskiplistNode { //节点对应成员的对象,一般是SDS robj *obj; //分数,跳跃表的顺序按照分值进行排列 double score; //存储后继节点的指针 struct zskiplistNode *backward;...
  • Redis zset

    2019-04-09 23:24:25
    Redis zset 1、zset特点 (1)zset有序集合结构 (2)set集合 VS zset有序集合 2、zset重要API (1)zadd 执行zadd user:1:ranking 225 tom操作: (2)zrem 执行zrem user:1:ranki...

    Redis下一小节:3-1 Redis Java客户端

    Redis zset

    1、zset特点

    (1)zset有序集合结构

    (2)set集合 VS zset有序集合

    2、zset重要API

    (1)zadd

           执行zadd user:1:ranking 225 tom操作:

    (2)zrem

           执行zrem user:1:ranking tom操作:

    (3)zscore

    执行zscore user:1:ranking tom操作:

    (4)zincrby

           执行zincrby user:1:ranking 100 tom操作:

    (5)zcard

           执行zcard user:1:ranking操作:

            命令操作演示:

    (6)zrange

           执行zrange user:1:ranking 0 -1 withscores操作:

    (7)zrangebyscore

           执行zrangebyscore user:1:ranking 100 200 withscores操作:

    (8)zcount

           执行zcount user:1:ranking 0 200操作:

    (9)zremrangebyrank

           执行zremrangebyrank user:1:ranking 4 5操作:

    (10)zremrangebyscore

           执行zremrangebyscore user:1:ranking 90 210操作:

            命令操作演示:

    3、zset实战

    实战-排行榜

    音乐排行榜、书单排行榜、电商平台各种商品排行榜等……

           score:timestamp saleCount followCount

    4、zset查缺补漏

    (1)zrevrank(从高到底排列)

    (2)zrevrange(根据索引从低到高查询范围)

    (3)zrevrangebyscore(根据分数从高到低查询范围)

    (4)zinterstore(两个集合分数交集)

     

    (5)zunionstore(两个集合分数并集)

    展开全文
  • 0.前言Redis有序集合ZSet可以按分数进行排序, 存储结构可能使用ziplist,skiplist和hash表, zset_max_ziplist_entries和zset_max_ziplist_value两个字段控制zset采用何种存储方式, zset_max_ziplist_entries表示...

    0.前言

    Redis有序集合ZSet可以按分数进行排序, 存储结构可能使用ziplist,skiplist和hash表, zset_max_ziplist_entries和zset_max_ziplist_value两个字段控制zset采用何种存储方式, zset_max_ziplist_entries表示ziplist中存储score和member占用的内存空间超过该值, 则存储结构会转变为skiplist和hash表; zset_max_ziplist_value表示ziplist中存储的member值占用的内存空间超过该值, 则存储结构会转变为skiplist和hash表. 存储使用ziplist时, ziplist存储格式为[member, score, member, score....], 以score值升序进行排序.存储使用skiplist时, 需要hash表配合使用, hash表存储以member为key, score为值, 加快member检索score速度; skiplist存储score和member, 并以score值进行升序排序.

    1.目录

    2.ZADD命令

    添加元素到有序集合中, 命令格式 : ZADD key score member [[score member] [score member] ...], 入口函数zaddCommand

    void zaddCommand(redisClient *c) {

    zaddGenericCommand(c,0);

    }

    /*函数向有序集合中添加一个元素, 在incr值设置时, 同时可以实现对score值进行累加操作*/

    void zaddGenericCommand(redisClient *c, int incr) {

    static char *nanerr = "resulting score is not a number (NaN)";

    robj *key = c->argv[1];

    robj *ele;

    robj *zobj;

    robj *curobj;

    double score = 0, *scores = NULL, curscore = 0.0;

    int j, elements = (c->argc-2)/2;

    int added = 0, updated = 0;

    if (c->argc % 2) {

    addReply(c,shared.syntaxerr);

    return;

    }

    /* 获取scores值, score必须为数字, 否则直接返回错误*/

    scores = zmalloc(sizeof(double)*elements);

    for (j = 0; j < elements; j++) {

    if (getDoubleFromObjectOrReply(c,c->argv[2+j*2],&scores[j],NULL)

    != REDIS_OK) goto cleanup;

    }

    /* 如果有序集合不存在, 直接进行创建 */

    zobj = lookupKeyWrite(c->db,key);

    if (zobj == NULL) {

    /*对限制条件进行判断,选择存储结构*/

    if (server.zset_max_ziplist_entries == 0 ||

    server.zset_max_ziplist_value < sdslen(c->argv[3]->ptr))

    {

    /*创建有序集合, 存储结构式skiplist*/

    zobj = createZsetObject();

    } else {

    /*创建有序集合, 存储结构式ziplist*/

    zobj = createZsetZiplistObject();

    }

    dbAdd(c->db,key,zobj);

    } else {

    if (zobj->type != REDIS_ZSET) {

    addReply(c,shared.wrongtypeerr);

    goto cleanup;

    }

    }

    for (j = 0; j < elements; j++) {

    score = scores[j];

    if (zobj->encoding == REDIS_ENCODING_ZIPLIST) {

    unsigned char *eptr;

    /* 在skiplist中进行查找, 找到则删除原来的, 插入新的, 否则直接进行插入操作*/

    ele = c->argv[3+j*2];

    if ((eptr = zzlFind(zobj->ptr,ele,&curscore)) != NULL) {

    /*incr值设置, 则需要进行累加*/

    if (incr) {

    score += curscore;

    if (isnan(score)) {

    addReplyError(c,nanerr);

    goto cleanup;

    }

    }

    /* 如果member和score都没有变化, 则不进行任何操作*/

    if (score != curscore) {

    zobj->ptr = zzlDelete(zobj->ptr,eptr);

    zobj->ptr = zzlInsert(zobj->ptr,ele,score);

    server.dirty++;

    updated++;

    }

    } else {

    /* 同样插入元素时进行检测ziplist转skiplist的阀值*/

    zobj->ptr = zzlInsert(zobj->ptr,ele,score);

    if (zzlLength(zobj->ptr) > server.zset_max_ziplist_entries)

    zsetConvert(zobj,REDIS_ENCODING_SKIPLIST);

    if (sdslen(ele->ptr) > server.zset_max_ziplist_value)

    zsetConvert(zobj,REDIS_ENCODING_SKIPLIST);

    server.dirty++;

    added++;

    }

    } else if (zobj->encoding == REDIS_ENCODING_SKIPLIST) {

    zset *zs = zobj->ptr;

    zskiplistNode *znode;

    dictEntry *de;

    /*存储结构为skiplist时, 首先从hash表中通过member查找到score, 同样找到删除原来的, 找不到则直接插入*/

    ele = c->argv[3+j*2] = tryObjectEncoding(c->argv[3+j*2]);

    de = dictFind(zs->dict,ele);

    if (de != NULL) {

    curobj = dictGetKey(de);

    curscore = *(double*)dictGetVal(de);

    if (incr) {

    score += curscore;

    if (isnan(score)) {

    addReplyError(c,nanerr);

    goto cleanup;

    }

    }

    /* member和score完全一样, 则不进行任何操作*/

    if (score != curscore) {

    redisAssertWithInfo(c,curobj,zslDelete(zs->zsl,curscore,curobj));

    znode = zslInsert(zs->zsl,score,curobj);

    incrRefCount(curobj); /* Re-inserted in skiplist. */

    dictGetVal(de) = &znode->score; /* Update score ptr. */

    server.dirty++;

    updated++;

    }

    } else {

    znode = zslInsert(zs->zsl,score,ele);

    incrRefCount(ele); /* Inserted in skiplist. */

    redisAssertWithInfo(c,NULL,dictAdd(zs->dict,ele,&znode->score) == DICT_OK);

    incrRefCount(ele); /* Added to dictionary. */

    server.dirty++;

    added++;

    }

    } else {

    redisPanic("Unknown sorted set encoding");

    }

    }

    if (incr) /* ZINCRBY */

    addReplyDouble(c,score);

    else /* ZADD */

    addReplyLongLong(c,added);

    cleanup:

    zfree(scores);

    if (added || updated) {

    signalModifiedKey(c->db,key);

    notifyKeyspaceEvent(REDIS_NOTIFY_ZSET,

    incr ? "zincr" : "zadd", key, c->db->id);

    }

    }

    ZCOUNT命令

    统计score值在一个范围内的元素数量, 命令格式: ZCOUNT key min max, zcount操作其实很简单, ziplist存储结构, 只需要依次遍历然后比较score值是否在范围内, 并记录满足条件的元素个数即可. skiplist可以对score值进行快速检索, 因此可以找到落入范围内开始元素和结束元素排名, 通过简单运算可以得出满足条件的元素数量.

    void zcountCommand(redisClient *c) {

    robj *key = c->argv[1];

    robj *zobj;

    zrangespec range;

    int count = 0;

    /* 解析min和max参数值, 并放入range中 */

    if (zslParseRange(c->argv[2],c->argv[3],&range) != REDIS_OK) {

    addReplyError(c,"min or max is not a float");

    return;

    }

    /*查找有序集合*/

    if ((zobj = lookupKeyReadOrReply(c, key, shared.czero)) == NULL ||

    checkType(c, zobj, REDIS_ZSET)) return;

    if (zobj->encoding == REDIS_ENCODING_ZIPLIST) {

    unsigned char *zl = zobj->ptr;

    unsigned char *eptr, *sptr;

    double score;

    /* 查找第一个位于range范围内的元素*/

    eptr = zzlFirstInRange(zl,&range);

    /* 找不到直接返回空 */

    if (eptr == NULL) {

    addReply(c, shared.czero);

    return;

    }

    /* 找到第一个符号条件元素, 然后依次遍历ziplist对符合条件的元素进行计数*/

    sptr = ziplistNext(zl,eptr);

    score = zzlGetScore(sptr);

    redisAssertWithInfo(c,zobj,zslValueLteMax(score,&range));

    while (eptr) {

    score = zzlGetScore(sptr);

    /* score必须小于给定返回的最大值max, 否则计数结束 */

    if (!zslValueLteMax(score,&range)) {

    break;

    } else {

    count++;

    zzlNext(zl,&eptr,&sptr);

    }

    }

    } else if (zobj->encoding == REDIS_ENCODING_SKIPLIST) {

    zset *zs = zobj->ptr;

    zskiplist *zsl = zs->zsl;

    zskiplistNode *zn;

    unsigned long rank;

    /* skiplist中查找第一个落入范围的元素 */

    zn = zslFirstInRange(zsl, &range);

    if (zn != NULL) {

    /*首先计算出大于min的元素个数count, rank获取的是大于min值第一个元素排名*/

    rank = zslGetRank(zsl, zn->score, zn->obj);

    count = (zsl->length - (rank - 1));

    /* skiplist中查找最后一个落入范围内的元素 */

    zn = zslLastInRange(zsl, &range);

    if (zn != NULL) {

    /*rank获取的是最后一个落入返回内的元素排名*/

    rank = zslGetRank(zsl, zn->score, zn->obj);

    /*(zsl->length-rank)表示所有大于max元素数量, 与count做减法计算出结果*/

    count -= (zsl->length - rank);

    }

    }

    } else {

    redisPanic("Unknown sorted set encoding");

    }

    addReplyLongLong(c, count);

    }

    ZRANGE命令

    获取一个位置范围内的元素, 命令格式: ZRANGE key start stop [WITHSCORES], start, stop元素代表位置下标, 从0开始. 这里只对range操作进行讲述, 其他的range操作大同小异, 只是对增加了一些判断的条件参数, 不在展开一一说明.

    void zrangeCommand(redisClient *c) {

    zrangeGenericCommand(c,0);

    }

    /*求range范围内元素*/

    void zrangeGenericCommand(redisClient *c, int reverse) {

    robj *key = c->argv[1];

    robj *zobj;

    int withscores = 0;

    long start;

    long end;

    int llen;

    int rangelen;

    /*取出start和stop值*/

    if ((getLongFromObjectOrReply(c, c->argv[2], &start, NULL) != REDIS_OK) ||

    (getLongFromObjectOrReply(c, c->argv[3], &end, NULL) != REDIS_OK)) return;

    /*设置withscores标志位*/

    if (c->argc == 5 && !strcasecmp(c->argv[4]->ptr,"withscores")) {

    withscores = 1;

    } else if (c->argc >= 5) {

    addReply(c,shared.syntaxerr);

    return;

    }

    if ((zobj = lookupKeyReadOrReply(c,key,shared.emptymultibulk)) == NULL

    || checkType(c,zobj,REDIS_ZSET)) return;

    /*由于start和end可以是负值, 全部进行转换为正值*/

    llen = zsetLength(zobj);

    if (start < 0) start = llen+start;

    if (end < 0) end = llen+end;

    if (start < 0) start = 0;

    /* 判断range范围是否符合条件,不合条件直接返回空 */

    if (start > end || start >= llen) {

    addReply(c,shared.emptymultibulk);

    return;

    }

    /*下标超出范围则置为为集合结尾元素位置*/

    if (end >= llen) end = llen-1;

    rangelen = (end-start)+1;

    /* Return the result in form of a multi-bulk reply */

    addReplyMultiBulkLen(c, withscores ? (rangelen*2) : rangelen);

    if (zobj->encoding == REDIS_ENCODING_ZIPLIST) {

    unsigned char *zl = zobj->ptr;

    unsigned char *eptr, *sptr;

    unsigned char *vstr;

    unsigned int vlen;

    long long vlong;

    /* ziplist首先找到start位置的元素, 然后依次遍历rangelen个元素, 返回给客户端*/

    if (reverse)

    eptr = ziplistIndex(zl,-2-(2*start));

    else

    eptr = ziplistIndex(zl,2*start);

    redisAssertWithInfo(c,zobj,eptr != NULL);

    sptr = ziplistNext(zl,eptr);

    while (rangelen--) {

    redisAssertWithInfo(c,zobj,eptr != NULL && sptr != NULL);

    redisAssertWithInfo(c,zobj,ziplistGet(eptr,&vstr,&vlen,&vlong));

    if (vstr == NULL)

    addReplyBulkLongLong(c,vlong);

    else

    addReplyBulkCBuffer(c,vstr,vlen);

    if (withscores)

    addReplyDouble(c,zzlGetScore(sptr));

    if (reverse)

    zzlPrev(zl,&eptr,&sptr);

    else

    zzlNext(zl,&eptr,&sptr);

    }

    } else if (zobj->encoding == REDIS_ENCODING_SKIPLIST) {

    zset *zs = zobj->ptr;

    zskiplist *zsl = zs->zsl;

    zskiplistNode *ln;

    robj *ele;

    /* skiplist同样根据start位置, 找到相应的元素, 遍历rangelen个元素返回给客户端*/

    if (reverse) {

    ln = zsl->tail;

    if (start > 0)

    ln = zslGetElementByRank(zsl,llen-start);

    } else {

    ln = zsl->header->level[0].forward;

    if (start > 0)

    ln = zslGetElementByRank(zsl,start+1);

    }

    while(rangelen--) {

    redisAssertWithInfo(c,zobj,ln != NULL);

    ele = ln->obj;

    addReplyBulk(c,ele);

    if (withscores)

    addReplyDouble(c,ln->score);

    ln = reverse ? ln->backward : ln->level[0].forward;

    }

    } else {

    redisPanic("Unknown sorted set encoding");

    }

    }

    交集并集命令

    求交集zinterstore, 求并集zunionstore, 两个命令操作相对比较复杂, 操作使用的是同一个函数, 命令格式如下, 非常类似.

    zinterstor命令格式 : ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]

    zunionstore命令格式: ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]

    /*有序集合求并集入口函数*/

    void zunionstoreCommand(redisClient *c) {

    zunionInterGenericCommand(c,c->argv[1], REDIS_OP_UNION);

    }

    /*有序集合求交集入口函数*/

    void zinterstoreCommand(redisClient *c) {

    zunionInterGenericCommand(c,c->argv[1], REDIS_OP_INTER);

    }

    #define REDIS_AGGR_SUM 1 //求和操作

    #define REDIS_AGGR_MIN 2 //取最小值

    #define REDIS_AGGR_MAX 3 //取最大值

    #define zunionInterDictValue(_e) (dictGetVal(_e) == NULL ? 1.0 : *(double*)dictGetVal(_e))

    /*聚合操作函数, 比较大小和求和操作*/

    inline static void zunionInterAggregate(double *target, double val, int aggregate) {

    if (aggregate == REDIS_AGGR_SUM) {

    *target = *target + val;

    /* The result of adding two doubles is NaN when one variable

    * is +inf and the other is -inf. When these numbers are added,

    * we maintain the convention of the result being 0.0. */

    if (isnan(*target)) *target = 0.0;

    } else if (aggregate == REDIS_AGGR_MIN) {

    *target = val < *target ? val : *target;

    } else if (aggregate == REDIS_AGGR_MAX) {

    *target = val > *target ? val : *target;

    } else {

    /* safety net */

    redisPanic("Unknown ZUNION/INTER aggregate type");

    }

    }

    /*具体进行并集和交集操作的函数*/

    void zunionInterGenericCommand(redisClient *c, robj *dstkey, int op) {

    int i, j;

    long setnum;

    int aggregate = REDIS_AGGR_SUM;

    zsetopsrc *src;

    zsetopval zval;

    robj *tmp;

    unsigned int maxelelen = 0;

    robj *dstobj;

    zset *dstzset;

    zskiplistNode *znode;

    int touched = 0;

    /* 获取表示key数量的numkeys字段 */

    if ((getLongFromObjectOrReply(c, c->argv[2], &setnum, NULL) != REDIS_OK))

    return;

    if (setnum < 1) {

    addReplyError(c,

    "at least 1 input key is needed for ZUNIONSTORE/ZINTERSTORE");

    return;

    }

    /* numkeys字段大于实际输入的key数量, 直接返回语法错误提示 */

    if (setnum > c->argc-3) {

    addReply(c,shared.syntaxerr);

    return;

    }

    /* 读取所有的key对应的集合 */

    src = zcalloc(sizeof(zsetopsrc) * setnum);

    for (i = 0, j = 3; i < setnum; i++, j++) {

    robj *obj = lookupKeyWrite(c->db,c->argv[j]);

    if (obj != NULL) {

    if (obj->type != REDIS_ZSET && obj->type != REDIS_SET) {

    zfree(src);

    addReply(c,shared.wrongtypeerr);

    return;

    }

    src[i].subject = obj;

    src[i].type = obj->type;

    src[i].encoding = obj->encoding;

    } else {

    src[i].subject = NULL;

    }

    /*weight默认为1*/

    src[i].weight = 1.0;

    }

    /* 如果后面还有参数, 解析剩余参数weights和aggregate字段 */

    if (j < c->argc) {

    int remaining = c->argc - j;

    while (remaining) {

    if (remaining >= (setnum + 1) && !strcasecmp(c->argv[j]->ptr,"weights")) {

    j++; remaining--;

    for (i = 0; i < setnum; i++, j++, remaining--) {

    if (getDoubleFromObjectOrReply(c,c->argv[j],&src[i].weight,

    "weight value is not a float") != REDIS_OK)

    {

    zfree(src);

    return;

    }

    }

    } else if (remaining >= 2 && !strcasecmp(c->argv[j]->ptr,"aggregate")) {

    j++; remaining--;

    if (!strcasecmp(c->argv[j]->ptr,"sum")) {

    aggregate = REDIS_AGGR_SUM;

    } else if (!strcasecmp(c->argv[j]->ptr,"min")) {

    aggregate = REDIS_AGGR_MIN;

    } else if (!strcasecmp(c->argv[j]->ptr,"max")) {

    aggregate = REDIS_AGGR_MAX;

    } else {

    zfree(src);

    addReply(c,shared.syntaxerr);

    return;

    }

    j++; remaining--;

    } else {

    zfree(src);

    addReply(c,shared.syntaxerr);

    return;

    }

    }

    }

    /* 对集合按集合元素多少进行升序排列 */

    qsort(src,setnum,sizeof(zsetopsrc),zuiCompareByCardinality);

    /*创建一个新的集合存放计算结果*/

    dstobj = createZsetObject();

    dstzset = dstobj->ptr;

    memset(&zval, 0, sizeof(zval));

    if (op == REDIS_OP_INTER) {

    /* 最少元素集合为空直接跳过不执行 */

    if (zuiLength(&src[0]) > 0) {

    /* 类似于无序集合求交集, 遍历第一个集合, 并在剩余的集合中查找, 查找不到则跳过该元素, 全部查找到则将该元素放入结果集合dstzset中*/

    zuiInitIterator(&src[0]);

    while (zuiNext(&src[0],&zval)) {

    double score, value;

    score = src[0].weight * zval.score;

    if (isnan(score)) score = 0;

    for (j = 1; j < setnum; j++) {

    /* 如果后面集合中有和第一个集合和第一个集合是同一个集合, 则特殊判断, 因为迭代操作不安全 */

    if (src[j].subject == src[0].subject) {

    value = zval.score*src[j].weight;

    zunionInterAggregate(&score,value,aggregate);

    } else if (zuiFind(&src[j],&zval,&value)) {

    /* 找到元素, 然后score值与weight值做乘积, 最后进行聚合操作*/

    value *= src[j].weight;

    zunionInterAggregate(&score,value,aggregate);

    } else {

    break;

    }

    }

    /*只有待查元素在所有集合中都出现,才将此元素添加进结果集合中*/

    if (j == setnum) {

    tmp = zuiObjectFromValue(&zval);

    znode = zslInsert(dstzset->zsl,score,tmp);

    incrRefCount(tmp); /* added to skiplist */

    dictAdd(dstzset->dict,tmp,&znode->score);

    incrRefCount(tmp); /* added to dictionary */

    /*判断并存储最大元素长度, 后面判断是否需要转换数据结构*/

    if (tmp->encoding == REDIS_ENCODING_RAW)

    if (sdslen(tmp->ptr) > maxelelen)

    maxelelen = sdslen(tmp->ptr);

    }

    }

    zuiClearIterator(&src[0]);

    }

    } else if (op == REDIS_OP_UNION) {

    dict *accumulator = dictCreate(&setDictType,NULL);

    dictIterator *di;

    dictEntry *de;

    double score;

    if (setnum) {

    /*为了尽可能的减少rehash操作, 扩展存放结果字典空间为最后一个集合的大小, 上面已经排序过, 最后一个是最大的集合*/

    dictExpand(accumulator,zuiLength(&src[setnum-1]));

    }

    /* 下面开始循环所有集合, 并在accumulator中查找, 如果找到则进行相应的运算, 否则直接插入accumulator中*/

    for (i = 0; i < setnum; i++) {

    if (zuiLength(&src[i]) == 0) continue;

    zuiInitIterator(&src[i]);

    while (zuiNext(&src[i],&zval)) {

    /* Initialize value */

    score = src[i].weight * zval.score;

    if (isnan(score)) score = 0;

    /* 查找元素是否已经在accumulator字典中 */

    de = dictFind(accumulator,zuiObjectFromValue(&zval));

    if (de == NULL) {

    tmp = zuiObjectFromValue(&zval);

    /* 记录元素最长的值, 后面用于判断是否需要对集合进行转换*/

    if (tmp->encoding == REDIS_ENCODING_RAW) {

    if (sdslen(tmp->ptr) > maxelelen)

    maxelelen = sdslen(tmp->ptr);

    }

    /* 直接添加到字典中 */

    de = dictAddRaw(accumulator,tmp);

    incrRefCount(tmp);

    dictSetDoubleVal(de,score);

    } else {

    /* 元素存在,按照指定的规则进行运算 */

    zunionInterAggregate(&de->v.d,score,aggregate);

    }

    }

    zuiClearIterator(&src[i]);

    }

    /*遍历将accumulator字典转化为有序集合*/

    di = dictGetIterator(accumulator);

    dictExpand(dstzset->dict,dictSize(accumulator));

    while((de = dictNext(di)) != NULL) {

    robj *ele = dictGetKey(de);

    score = dictGetDoubleVal(de);

    znode = zslInsert(dstzset->zsl,score,ele);

    incrRefCount(ele); /* added to skiplist */

    dictAdd(dstzset->dict,ele,&znode->score);

    incrRefCount(ele); /* added to dictionary */

    }

    dictReleaseIterator(di);

    /* We can free the accumulator dictionary now. */

    dictRelease(accumulator);

    } else {

    redisPanic("Unknown operator");

    }

    /*存储目标key存在,则删除原来的集合*/

    if (dbDelete(c->db,dstkey)) {

    signalModifiedKey(c->db,dstkey);

    touched = 1;

    server.dirty++;

    }

    if (dstzset->zsl->length) {

    /* 判断是否需要将存储结构转换为ziplist */

    if (dstzset->zsl->length <= server.zset_max_ziplist_entries &&

    maxelelen <= server.zset_max_ziplist_value)

    zsetConvert(dstobj,REDIS_ENCODING_ZIPLIST);

    dbAdd(c->db,dstkey,dstobj);

    addReplyLongLong(c,zsetLength(dstobj));

    if (!touched) signalModifiedKey(c->db,dstkey);

    notifyKeyspaceEvent(REDIS_NOTIFY_ZSET,

    (op == REDIS_OP_UNION) ? "zunionstore" : "zinterstore",

    dstkey,c->db->id);

    server.dirty++;

    } else {

    decrRefCount(dstobj);

    addReply(c,shared.czero);

    if (touched)

    notifyKeyspaceEvent(REDIS_NOTIFY_GENERIC,"del",dstkey,c->db->id);

    }

    zfree(src);

    }

    展开全文
  • StringRedisTemplate zset

    2021-03-02 11:18:40
    springboot redis zset test demo package net.yanlp.mid.ch.test; import lombok.extern.slf4j.Slf4j; import net.yanlp.mid.ch.MidCh; import org.junit.jupiter.api.Test; import org.junit.jupiter.api....

    springboot redis zset test demo

    package net.yanlp.mid.ch.test;
    
    import lombok.extern.slf4j.Slf4j;
    import net.yanlp.mid.ch.MidCh;
    import org.junit.jupiter.api.Test;
    import org.junit.jupiter.api.extension.ExtendWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.test.context.ActiveProfiles;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit.jupiter.SpringExtension;
    
    import java.time.Duration;
    import java.util.Set;
    
    @Slf4j
    @SpringBootTest
    @ExtendWith(SpringExtension.class)
    @ContextConfiguration(classes = MidCh.class)
    @ActiveProfiles("tmp")
    public class StringRedisTemplateTest {
        @Autowired
        private StringRedisTemplate stringRedisTemplate;
    
        public static final long REDIS_CACHE = 60;
        public static final long EFFECTIVE_TIME = 120; // 2min中内数据有效
    
        @Test
        public void zSetTest() {
            String key = "zset:test:key1";
            long now = System.currentTimeMillis();
            for (int i = 0; i <= 10; i++) {
                stringRedisTemplate.opsForZSet().add(key, "yanlp" + i, now - i);
            }
            stringRedisTemplate.opsForZSet().add(key, "yanlp", now - 130 * 1000);
            stringRedisTemplate.expire(key, Duration.ofSeconds(REDIS_CACHE));
    
            long less = now - EFFECTIVE_TIME * 1000;
            // 1. 判断是否有效
            Double score = stringRedisTemplate.opsForZSet().score(key, "yanlp");
            if (score == null || score <= less) {
                log.info("yanlp无效");
            }
    
            score = stringRedisTemplate.opsForZSet().score(key, "yanlp1");
            if (score != null || score >= less) {
                log.info("yanlp1有效");
            }
            // 2. 有效数量
            Long total = stringRedisTemplate.opsForZSet().count(key, less, now);
            if (total == null) {
                total = 0L;
            }
            log.info("有效数量: [{}]", total);
    
            // 3. 分页
            int pageNo = 1;
            int pageSize = 2;
            Set<String> users = stringRedisTemplate.opsForZSet().rangeByScore(key, less, now, (pageNo - 1) * pageSize, pageSize);
            log.info("分页: [{}]", users);
    
            Set<String> reserveUsers = stringRedisTemplate.opsForZSet().reverseRangeByScore(key, less, now, (pageNo - 1) * pageSize, pageSize);
            log.info("倒序分页: [{}]", reserveUsers);
    
            // 4. 删除
            stringRedisTemplate.opsForZSet().remove(key, "yanlp");
            score = stringRedisTemplate.opsForZSet().score(key, "yanlp");
            if (score == null) {
                log.info("yanlp已删除");
            }
    
        }
    }
    
    
    展开全文
  • 将知识从定义、来源、实现、问题、优化、应用方面来系统性的回答Zset原理有序集合对象是有序的。与列表使用索引下标作为排序依据不同,有序集合为每个元素设置一个分数(score)作为排序依据ZSet底层如何实现一、使用...
  • zset类型

    2020-03-26 18:00:12
    zset类型 sorted set,有序集合 元素为string类型 元素具有唯⼀性,不重复 每个元素都会关联⼀个double类型的score,表示权重,通过权重将元素从⼩到⼤排序 说明:没有修改操作 增加 添加 zadd key score1 ...
  • zset 类型

    2019-05-16 11:50:00
    zset 常用命令 zadd key score element O(logn)element 不能重复,score 可以重复; zrem key element O(1) zscore key element zincrby key increScore element zcard key O(1) zrank key element zran...
  • ssdb zset

    2018-05-17 14:52:00
    zset name key value  key 可以与name 相同 但是value若与name相同会被置为0 转载于:https://www.cnblogs.com/hiraeth/p/9051083.html
  • sorted set类型sorted sets类型以及其操作zset是set的一格升级版本,它在set的基础上增加了一格顺序属性,这一属性在添加元素的同时可以指定,每次指定后,zset会自动重新按照新的值调整顺序。可以理解为有两列的...
  • redis:ZSet类型的操作命令1:zadd:添加元素,格式是zadd zset的key score值项的值,Score和项可以是多对,score可以是整数,也可以是浮点数,还可以是+inf表示正无穷大,-inf表示负无穷大2:zrange:获取索引区间内...
  • redis的zset

    2020-07-03 16:04:18
    zset适合排行榜,Topx这样的场景 zset的底层结构是跳跃表,而与之类似的Java中的有序Set是TreeSet,使用红黑树实现的。 concurrent包里面,还有一个类叫做ConcurrentSkipListMap,从它的名字就可以看出来,也是用...
  • 10303 redis数据类型zset redis zset :有序集合是string类型元素的集合,且不允许重复的成员.每个元素都会关联一个double类型的分数,redis正是通过分数来为集合中的成员进行从小到大的排序.有序集合的成员是唯一的,但...
  • Redis Zset跳表

    2021-03-11 11:58:05
    在前面说Redis的文章里,提到了Redis的有序集合zset底层是依赖跳表实现的,当时没有展开讨论,内心认为还是需要一个专门的篇幅来介绍它。   先想一想为什么有序集合需要用跳表实现?回忆一下有序集合的几个关键词...
  • 前言最近把 AirNet 中的空气质量排行换成了用 Zset 实现,这篇笔记就来深入了解下 Zset 的底层实现原理。Zset 编码的选择在通过 ZADD 命令添加第一个元素到空 key 时, Redis 会通过检查输入的第一个元素来决定使用...
  • ZSet数据结构

    千次阅读 2019-08-14 16:40:30
    ZSet(sorted set-有序集合) 数据结构类似于Set结构,只是ZSet结构中,在set基础上加入了一个score字段,通过利用score和index来进行相关的排序。每个元 素都会有一个分值(score),然后所有元素按照分值的大小...
  • redis之Zset操作

    2020-12-22 17:22:33
    redis之Zset操作 集合Zset:在set基础上加了一个score。 Zset设置:ZADD zset01 60 v1 70 v2 80 v3 90 v4 100 v5 Zset获取等级:ZRANGE zset01 0 -1 Zset逆序获取等级:ZREVRANGE zset01 0 -1 Zset获取分数和等级:...
  • redis zset类型

    2021-05-01 16:57:29
    redis的zset类型 又称为有序集合 ,它保留了集合元素不能重复的特性,与set类型不同的是 每一个字段都有一个分数,利用该分数作为排序的依据. 有序集合的可以利用分数从小到大排序,虽然有序集合的成员是唯一的,但是分数...
  • redis数据类型:zset(sorted set)类型和set一样sorted set也是string类型元素的集合,不同的是每个元素都会关联一个权。通过权值可以有序的获取集合中的元素。redis里面的zset类型被称为有序集合,意味着有序集合...
  • redis zset usage

    2019-10-06 02:17:16
    redis zset usage zset means sorted setit take with a score as sort standardswhen you use zadd() add a new element,you need specificed...
  • 1. 编码zset的编码有ziplist和skiplist两种。底层分别使用ziplist(压缩链表)和skiplist(跳表)实现。什么时候使用ziplist什么时候使用skiplist?当zset满足以下两个条件的时候,使用ziplist:保存的元素少于128个 ...
  • 序言Zset跟Set之间可以有并集运算,因为他们存储的数据字符串集合,不能有一样的成员出现在一个zset中,但是为什么有了set还要有zset呢?zset叫做有序集合,而set是无序的,zset怎么做到有序的呢?就是zset的每一个...
  • 一.Zset编码的选择1.有序集合对象的编码可以是ziplist或者skiplist。同时满足以下条件时使用ziplist编码:元素数量小于128个所有member的长度都小于64字节其他:不能满足上面两个条件的使用 skiplist 编码。以上两个...
  • redis的zset解析

    千次阅读 2020-05-02 15:36:47
    文章目录简介zset的常用命令zset结构分析ziplistdictskiplist 最近工作中使用了redis的zset数据结构,为了能够熟练清晰的运用这个数据结构,总结如下。 简介 zset是一个有序集合,每一个成员有一个分数与之对应,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 11,662
精华内容 4,664
热门标签
关键字:

zset