八redis的sortset数据类型常见命令、内部编码、场景

你的名字 2023-03-13 05:51 35阅读 0赞

八redis的sortset数据类型常见命令、内部编码、场景

有序保存多个不重复的字符串的集合。

常见命令

zadd

  • 解释

    有序集合插入元素以及元素的分数(分数支持双精度浮点数)。
    如果本次插入的元素已经存在,则更新元素的分数,并且重排序。
    如果键不存在,则创建Key。
    如果其中元素的分数相同,则按照字典序排序。

  • 用法 zadd key [NX|XX] [CH][INCR] socre member [score member]
    NX: 仅仅更新元素的分数,元素必须存在才能操作成功
    XX: 仅仅添加元素及其分数,当元素存在,则不操作
  • 示例

    127.0.0.1:6379> zadd key1 1 one
    (integer) 1
    127.0.0.1:6379> zadd key1 2 two
    (integer) 1
    127.0.0.1:6379> zrange key1 0 -1 withscores
    1)”one”
    2)”1”
    3)”two”
    4)”2”

  • 源码

    / redis.c **/
    struct redisCommand readonlyCommandTable[] = {
    { “zadd”,zaddCommand,4,REDIS_CMD_DENYOOM,NULL,1,1,1}
    }

  1. 赋值要设置元素的分数给变量scoreval
  2. 在dict中查找键是否存在
  3. 键不存在,创建数据类型为zset的键其内部编码为跳表,加入键到dict中
  4. 键存在,如果数据类型不是zset返回错误信息
  5. 如果命令为zadd,则incr=0;命令为zincrby,则incr=1,则进入下面代码块
    5.1 如果元素存在,新的score等于老值加本次增加的score
    5.2 如果score的值不是数字类型,返回错误信息
  6. 如果元素不存在,新插入元素
  7. 如果元素存在,删除元素重新插入元素及其分数

    / t_zset.c /
    void zaddCommand(redisClient *c) {

    1. double scoreval;
    2. // 1.赋值要设置元素的分数给变量scoreval
    3. if (getDoubleFromObjectOrReply(c,c->argv[2],&scoreval,NULL) != REDIS_OK) return;
    4. c->argv[3] = tryObjectEncoding(c->argv[3]);
    5. zaddGenericCommand(c,c->argv[1],c->argv[3],scoreval,0);

    }

  1. void zaddGenericCommand(redisClient *c, robj *key, robj *ele, double score, int incr) {
  2. robj *zsetobj;
  3. zset *zs;
  4. zskiplistNode *znode;
  5. //2. 在dict中查找键是否存在
  6. zsetobj = lookupKeyWrite(c->db,key);
  7. if (zsetobj == NULL) {
  8. //3. 键不存在,创建数据类型为zset的键其内部编码为跳表,加入键到dict中
  9. zsetobj = createZsetObject();
  10. dbAdd(c->db,key,zsetobj);
  11. } else {
  12. //4. 键存在,如果数据类型不是zset返回错误信息
  13. if (zsetobj->type != REDIS_ZSET) {
  14. addReply(c,shared.wrongtypeerr);
  15. return;
  16. }
  17. }
  18. zs = zsetobj->ptr;
  19. // 5. 如果命令为zadd,则incr=0;命令为zincrby,则incr=1,则进入下面代码块
  20. if (incr) {
  21. dictEntry *de = dictFind(zs->dict,ele);
  22. // 5.1 如果元素存在,新的score等于老值加本次增加的score
  23. if (de != NULL)
  24. score += *(double*)dictGetEntryVal(de);
  25. //5.2 如果score的值不是数字类型,返回错误信息
  26. if (isnan(score)) {
  27. addReplyError(c,"resulting score is not a number (NaN)");
  28. return;
  29. }
  30. }
  31. //6. 如果元素不存在,新插入元素
  32. if (dictAdd(zs->dict,ele,NULL) == DICT_OK) {
  33. dictEntry *de;
  34. incrRefCount(ele); /* added to hash */
  35. znode = zslInsert(zs->zsl,score,ele);
  36. incrRefCount(ele); /* added to skiplist */
  37. /* Update the score in the dict entry */
  38. de = dictFind(zs->dict,ele);
  39. redisAssert(de != NULL);
  40. dictGetEntryVal(de) = &znode->score;
  41. touchWatchedKey(c->db,c->argv[1]);
  42. server.dirty++;
  43. if (incr)
  44. addReplyDouble(c,score);
  45. else
  46. addReply(c,shared.cone);
  47. } else {
  48. //7. 如果元素存在,删除元素重新插入元素及其分数
  49. dictEntry *de;
  50. robj *curobj;
  51. double *curscore;
  52. int deleted;
  53. /* Update score */
  54. de = dictFind(zs->dict,ele);
  55. redisAssert(de != NULL);
  56. curobj = dictGetEntryKey(de);
  57. curscore = dictGetEntryVal(de);
  58. /* When the score is updated, reuse the existing string object to * prevent extra alloc/dealloc of strings on ZINCRBY. */
  59. if (score != *curscore) {
  60. deleted = zslDelete(zs->zsl,*curscore,curobj);
  61. redisAssert(deleted != 0);
  62. znode = zslInsert(zs->zsl,score,curobj);
  63. incrRefCount(curobj);
  64. /* Update the score in the current dict entry */
  65. dictGetEntryVal(de) = &znode->score;
  66. touchWatchedKey(c->db,c->argv[1]);
  67. server.dirty++;
  68. }
  69. if (incr)
  70. addReplyDouble(c,score);
  71. else
  72. addReply(c,shared.czero);
  73. }
  74. }

zcard

  • 解释

    返回有序集合元素的个数,如果键不存在,返回0

  • 用法 zcard key
  • 示例

    127.0.0.1:6379> zadd key1 1 one
    (integer) 1
    127.0.0.1:6379> zadd key1 2 two
    (integer) 1
    127.0.0.1:6379> zcard key1
    (integer) 2

  • 源码

    / redis.c **/
    struct redisCommand readonlyCommandTable[] = {
    { “zcard”,zcardCommand,2,0,NULL,1,1,1}
    }

    void zcardCommand(redisClient *c) {

    1. robj *o;
    2. zset *zs;
    3. //1.检查键是否存在以及是否数据类型是sort set,否则返回0
    4. if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
    5. checkType(c,o,REDIS_ZSET)) return;
    6. //2. 获取到键中元素的指针,获取长度
    7. zs = o->ptr;
    8. addReplyLongLong(c,zs->zsl->length);

    }

zcount

  • 解释

    返回有序集合中元素分数在min和max中间的元素个数。
    min max 表示在min,max之间包含min,max,
    (min (max表示在min,max之间,不包含min,max
    min最小值是-inf,max最大值是+inf

  • 用法 zcount key min max
  • 示例

    127.0.0.1:6379> zadd key1 1 one
    (integer)1
    127.0.0.1:6379> zadd key1 2 two
    (integer)1
    127.0.0.1:6379> zcount key1 1 2
    (integer)2
    127.0.0.1:6379> zcount key1 -inf +inf
    (integer)2

  • 源码

    / redis.c **/
    struct redisCommand readonlyCommandTable[] = {
    { “zcount”,zcountCommand,4,0,NULL,1,1,1}
    }

  1. 解析min.max的值
  2. ZRANGEBYSCORE, ZREVRANGEBYSCORE命令可能是有超过四个参数的,需要解析出来
  3. 检查键是否存在以及是否数据类型是sort set,否则返回0
  4. zset的对象
  5. 获取跳表中等于min或者大于min的分数元素指针
  6. reverse=0 分数从低到高,reverse=1 分数从高到低
  7. 没有范围内的元素,直接返回
  8. zcount命令 limit = -1,offset = 0 ln表示等于或大于start的元素的指针,根据正反序,以及是否包含stop进行判断
  9. 元素在范围内计数

    / t_zset.c **/

    void zcountCommand(redisClient *c) {

    1. genericZrangebyscoreCommand(c,0,1);

    }

  1. void genericZrangebyscoreCommand(redisClient *c, int reverse, int justcount) {
  2. zrangespec range;
  3. robj *o, *emptyreply;
  4. zset *zsetobj;
  5. zskiplist *zsl;
  6. zskiplistNode *ln;
  7. int offset = 0, limit = -1;
  8. int withscores = 0;
  9. unsigned long rangelen = 0;
  10. void *replylen = NULL;
  11. // 1.解析min.max的值
  12. if (zslParseRange(c->argv[2],c->argv[3],&range) != REDIS_OK) {
  13. addReplyError(c,"min or max is not a double");
  14. return;
  15. }
  16. //2. ZRANGEBYSCORE, ZREVRANGEBYSCORE命令可能是有超过四个参数的,需要解析出来
  17. if (c->argc > 4) {
  18. int remaining = c->argc - 4;
  19. int pos = 4;
  20. while (remaining) {
  21. if (remaining >= 1 && !strcasecmp(c->argv[pos]->ptr,"withscores")) {
  22. pos++; remaining--;
  23. withscores = 1;
  24. } else if (remaining >= 3 && !strcasecmp(c->argv[pos]->ptr,"limit")) {
  25. offset = atoi(c->argv[pos+1]->ptr);
  26. limit = atoi(c->argv[pos+2]->ptr);
  27. pos += 3; remaining -= 3;
  28. } else {
  29. addReply(c,shared.syntaxerr);
  30. return;
  31. }
  32. }
  33. }
  34. //3. 检查键是否存在以及是否数据类型是sort set,否则返回0
  35. emptyreply = justcount ? shared.czero : shared.emptymultibulk;
  36. if ((o = lookupKeyReadOrReply(c,c->argv[1],emptyreply)) == NULL ||
  37. checkType(c,o,REDIS_ZSET)) return;
  38. //4. zset的对象
  39. zsetobj = o->ptr;
  40. zsl = zsetobj->zsl;
  41. //5. 获取跳表中等于min或者大于min的分数元素指针
  42. ln = zslFirstWithScore(zsl,range.min);
  43. //6. reverse=0 分数从低到高,reverse=1 分数从高到低
  44. if (reverse) {
  45. /* If range.min is out of range, ln will be NULL and we need to use * the tail of the skiplist as first node of the range. */
  46. if (ln == NULL) ln = zsl->tail;
  47. /* zslFirstWithScore returns the first element with where with * score >= range.min, so backtrack to make sure the element we use * here has score <= range.min. */
  48. while (ln && ln->score > range.min) ln = ln->backward;
  49. /* Move to the right element according to the range spec. */
  50. if (range.minex) {
  51. /* Find last element with score < range.min */
  52. while (ln && ln->score == range.min) ln = ln->backward;
  53. } else {
  54. /* Find last element with score <= range.min */
  55. while (ln && ln->level[0].forward &&
  56. ln->level[0].forward->score == range.min)
  57. ln = ln->level[0].forward;
  58. }
  59. } else {
  60. if (range.minex) {
  61. /* Find first element with score > range.min */
  62. while (ln && ln->score == range.min) ln = ln->level[0].forward;
  63. }
  64. }
  65. //7. 没有范围内的元素,直接返回
  66. if (ln == NULL) {
  67. addReply(c,emptyreply);
  68. return;
  69. }
  70. /* We don't know in advance how many matching elements there * are in the list, so we push this object that will represent * the multi-bulk length in the output buffer, and will "fix" * it later */
  71. if (!justcount)
  72. replylen = addDeferredMultiBulkLength(c);
  73. /* If there is an offset, just traverse the number of elements without * checking the score because that is done in the next loop. */
  74. while(ln && offset--) {
  75. if (reverse)
  76. ln = ln->backward;
  77. else
  78. ln = ln->level[0].forward;
  79. }
  80. //8. zcount命令 limit = -1,offset = 0 ln表示等于或大于start的元素的指针
  81. // 根据正反序,以及是否包含stop进行判断
  82. while (ln && limit--) {
  83. /* Check if this this element is in range. */
  84. if (reverse) {
  85. if (range.maxex) {
  86. /* Element should have score > range.max */
  87. if (ln->score <= range.max) break;
  88. } else {
  89. /* Element should have score >= range.max */
  90. if (ln->score < range.max) break;
  91. }
  92. } else {
  93. if (range.maxex) {
  94. /* Element should have score < range.max */
  95. if (ln->score >= range.max) break;
  96. } else {
  97. /* Element should have score <= range.max */
  98. if (ln->score > range.max) break;
  99. }
  100. }
  101. //9. 元素在范围内计数
  102. rangelen++;
  103. if (!justcount) {
  104. addReplyBulk(c,ln->obj);
  105. if (withscores)
  106. addReplyDouble(c,ln->score);
  107. }
  108. if (reverse)
  109. ln = ln->backward;
  110. else
  111. ln = ln->level[0].forward;
  112. }
  113. if (justcount) {
  114. addReplyLongLong(c,(long)rangelen);
  115. } else {
  116. setDeferredMultiBulkLength(c,replylen,
  117. withscores ? (rangelen*2) : rangelen);
  118. }
  119. }

zincrby

  • 解释

    有序集合指定元素增加指定的分数,结果返回元素最终的值;
    如果元素不存在,则元素的分数为本次指定的值;
    如果键不存在,创建键后,增加元素并且指定其值为本次指定的值;
    如果键的类型不是有序集合(zset),则返回错误。

  • 用法 zincrby key increment member
  • 示例

    127.0.0.1:6379> zadd key1 1 one
    (integer)1
    127.0.0.1:6379> zincrby key1 3 one
    (integer)4
    127.0.0.1:6379> zincrby key1 5 three
    (integer)5
    127.0.0.1:6379> zrange key1 0 -1 withscores
    1)”one”
    2)”4”
    3)”three”
    4)”5”

  • 源码

    / redis.c **/
    struct redisCommand readonlyCommandTable[] = {
    { “zincrby”,zincrbyCommand,4,REDIS_CMD_DENYOOM,NULL,1,1,1}
    }

  1. /** ** t_zset.c 参考zadd **/
  2. void zincrbyCommand(redisClient *c) {
  3. double scoreval;
  4. if (getDoubleFromObjectOrReply(c,c->argv[2],&scoreval,NULL) != REDIS_OK) return;
  5. c->argv[3] = tryObjectEncoding(c->argv[3]);
  6. zaddGenericCommand(c,c->argv[1],c->argv[3],scoreval,1);
  7. }

zlexcount

  • 解释

    当有序集合的所有元素的分数是相同的时候,会强制使用字典序对元素进行排序。
    这个元素返回在start,stop之间的的元素个数。

  • 用法 zlexcount key min max
    min,max的书写必须使用(,[, ( 表示不包含, [ 表示包含。
    zlexcount - + 表示所有元素.
  • 示例

    127.0.0.1:6379> zadd key1 1 one
    (integer)
    127.0.0.1:6379> zadd key1 1 two
    (integer)1
    127.0.0.1:6379> zlexcount key1 - +
    (integer)2
    127.0.0.1:6379> zlexcount key1 [o [w
    (integer)2

zpopmax

  • 解释

    返回有序集合中分数最大的count的元素,并且从有序集合中删除这些元素
    如果count未指定,则操作分数最大的元素。
    注意:这个命令redis5.0.0及之后的版本才支持

  • 用法 zpopmax key [count]
  • 示例

    127.0.0.1:6379> zadd key1 1 one
    (integer)1
    127.0.0.1:6379> zadd key1 2 two
    (integer)1
    127.0.0.1:6379> zpopmax key1
    1)”two”
    2)”2”

zpopmin

  • 解释

    返回有序集合中分数最小的count的元素,并且从有序集合中删除这些元素
    如果count未指定,则操作分数最小的元素。
    注意:这个命令redis5.0.0及之后的版本才支持

  • 用法
  • 示例

    127.0.0.1:6379> zadd key1 1 one
    (integer)1
    127.0.0.1:6379> zadd key1 2 two
    (integer)1
    127.0.0.1:6379> zpopmix key1
    1)”one”
    2)”1”

  • 源码

zrange

  • 解释

    返回有序集合的元素,按照分数从低到高返回。
    如果元素的分数相同,按照元素的字典序排序;
    如果想返回元素的分数,使用withscores;
    start,stop从0开始(表示第一个元素),start,stop包含start,stop,
    如zrange key 0 1 返回索引为0,1之间的元素包含0,1;

  • 用法 zrange key start stop [witscores]
  • 示例

    127.0.0.1:6379> zadd key1 1 one
    (integer) 1
    127.0.0.1:6379> zadd key1 2 two
    (integer) 1
    127.0.0.1:6379> zrange key1 0 -1
    1)one
    2)two
    127.0.0.1:6379> zrange key1 0 -1 withscores
    1)”one”
    2)”1”
    3)”two”
    4)”2”

  • 源码

  1. 解析出statr,stop的值
  2. 解析传入的命令参数是否包含withscores需要返回元素对应的分数
  3. 如果命令的参数大于5,返回语法错误
  4. 查询键是否存在以及是否是sortset数据类型
  5. 转换存储的负的索引值
  6. 如果startd大于end或start超出了元素的个数返回错误信息
  7. 如果stop最大为元素个数的长度
  8. 获取到开始遍历的元素对象
  9. 返回元素,如果要返回分数也返回分数

    / redis.c **/
    struct redisCommand readonlyCommandTable[] = {
    { “zrange”,zrangeCommand,-4,0,NULL,1,1,1}
    }

    / t_zset.c **/

    void zrangeCommand(redisClient *c) {

    1. zrangeGenericCommand(c,0);

    }

    void zrangeGenericCommand(redisClient *c, int reverse) {

    1. robj *o;
    2. long start;
    3. long end;
    4. int withscores = 0;
    5. int llen;
    6. int rangelen, j;
    7. zset *zsetobj;
    8. zskiplist *zsl;
    9. zskiplistNode *ln;
    10. robj *ele;
    11. //1. 解析出statr,stop的值
    12. if ((getLongFromObjectOrReply(c, c->argv[2], &start, NULL) != REDIS_OK) ||
    13. (getLongFromObjectOrReply(c, c->argv[3], &end, NULL) != REDIS_OK)) return;
    14. //2. 解析传入的命令参数是否包含withscores需要返回元素对应的分数
    15. if (c->argc == 5 && !strcasecmp(c->argv[4]->ptr,"withscores")) {
    16. withscores = 1;
    17. } else if (c->argc >= 5) {
    18. //3. 如果命令的参数大于5,返回语法错误
    19. addReply(c,shared.syntaxerr);
    20. return;
    21. }
    22. //4. 查询键是否存在以及是否是sortset数据类型
    23. if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptymultibulk)) == NULL
    24. || checkType(c,o,REDIS_ZSET)) return;
    25. zsetobj = o->ptr;
    26. zsl = zsetobj->zsl;
    27. llen = zsl->length;
    28. //5. 转换存储的负的索引值
    29. if (start < 0) start = llen+start;
    30. if (end < 0) end = llen+end;
    31. if (start < 0) start = 0;
    32. //6. 如果startd大于end或start超出了元素的个数返回错误信息
    33. if (start > end || start >= llen) {
    34. addReply(c,shared.emptymultibulk);
    35. return;
    36. }
    37. //7. 如果stop最大为元素个数的长度
    38. if (end >= llen) end = llen-1;
    39. rangelen = (end-start)+1;
    40. //8. 获取到开始遍历的元素对象
    41. if (reverse) {
    42. ln = start == 0 ? zsl->tail : zslGetElementByRank(zsl, llen-start);
    43. } else {
    44. ln = start == 0 ?
    45. zsl->header->level[0].forward : zslGetElementByRank(zsl, start+1);
    46. }
    47. //9. 返回元素,如果要返回分数也返回分数
    48. addReplyMultiBulkLen(c,withscores ? (rangelen*2) : rangelen);
    49. for (j = 0; j < rangelen; j++) {
    50. ele = ln->obj;
    51. addReplyBulk(c,ele);
    52. if (withscores)
    53. addReplyDouble(c,ln->score);
    54. ln = reverse ? ln->backward : ln->level[0].forward;
    55. }

    }

zrevrange

  • 解释

    返回有序集合的元素,按照分数从高到低返回。
    如果元素的分数相同,按照元素的字典序排序;
    如果想返回元素的分数,使用withscores;
    start,stop从0开始(表示第一个元素),start,stop包含start,stop,
    zrevrange key 0 1 返回索引为0,1之间的元素包含0,1;

  • 用法 zrevrange key start stop [withscores]
  • 示例

    127.0.0.1:6379> zadd key1 1 one
    (integer) 1
    127.0.0.1:6379> zadd key1 2 two
    (integer) 1
    127.0.0.1:6379> zrange key1 0 -1
    1)one
    2)two
    127.0.0.1:6379> zrevrange key1 0 -1 withscores
    1)”two”
    2)”2”
    3)”one”
    4)”1”

  • 源码

    / redis.c **/
    struct redisCommand readonlyCommandTable[] = {
    { “zrevrange”,zrevrangeCommand,-4,0,NULL,1,1,1}
    }

    / 参见zrange t_zset.c /
    void zrevrangeCommand(redisClient *c) {

    1. zrangeGenericCommand(c,1);

    }

zrangebyscore

  • 解释

返回有序集合中的元素分数在start,stop(包含start,stop)之间按照分数从低到高排序,
如果分数相同,按照字典序排序,

  • 用法 zrangebyscore key min max [withscores] [limit offset count]

    withscores: 返回包含元素的分数
    min:最小是-inf
    max:最大是+inf
    min, max = -inf, +inf 表示返回所有的元素

  • 示例

    127.0.0.1:6379> zadd key1 1 one
    (integer) 1
    127.0.0.1:6379> zadd key1 2 two
    (integer) 1
    127.0.0.1:6379> zrange key1 0 -1
    1)one
    2)two
    127.0.0.1:6379> zrangebyscore key1 -inf +inf
    1)”one”
    2)”two”

  • 源码

    / redis.c **/
    struct redisCommand readonlyCommandTable[] = {
    { “zrangebyscore”,zrangebyscoreCommand,-4,0,NULL,1,1,1}
    }

    / 参见zcount t_zset.c /
    void zrangebyscoreCommand(redisClient *c) {

    1. genericZrangebyscoreCommand(c,0,0);

    }

zrank

  • 解释

返回有序集合中指定元素的索引,
索引从0开始,元素排序按照分数从低到高。
zrevrank按照分数从高到低排序。

  • 用法 zrank key member
  • 示例

    127.0.0.1:6379> zadd key1 1 one
    (integer) 1
    127.0.0.1:6379> zadd key1 3 two
    (integer) 1
    127.0.0.1:6379> zrank key1 two
    1)1

  • 源码

    / redis.c **/
    struct redisCommand readonlyCommandTable[] = {
    { “zrangebyscore”,zrangebyscoreCommand,-4,0,NULL,1,1,1}
    }

  1. 1.查找键是否存在并且是否是sortset数据类型
  2. 2.查找对应的元素
  3. 3.元素不存在返回空
  4. 4.查询元素的分数,以及其分数的索引
  5. /** ** ** t_zset.c **/
  6. void zrankCommand(redisClient *c) {
  7. zrankGenericCommand(c, 0);
  8. }
  9. void zrankGenericCommand(redisClient *c, int reverse) {
  10. robj *o;
  11. zset *zs;
  12. zskiplist *zsl;
  13. dictEntry *de;
  14. unsigned long rank;
  15. double *score;
  16. //1.查找键是否存在并且是否是sortset数据类型
  17. if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
  18. checkType(c,o,REDIS_ZSET)) return;
  19. zs = o->ptr;
  20. zsl = zs->zsl;
  21. //2. 查找对应的元素
  22. c->argv[2] = tryObjectEncoding(c->argv[2]);
  23. de = dictFind(zs->dict,c->argv[2]);
  24. //3. 元素不存在返回空
  25. if (!de) {
  26. addReply(c,shared.nullbulk);
  27. return;
  28. }
  29. //4. 查询元素的分数,以及其分数的索引
  30. score = dictGetEntryVal(de);
  31. rank = zslGetRank(zsl, *score, c->argv[2]);
  32. if (rank) {
  33. if (reverse) {
  34. addReplyLongLong(c, zsl->length - rank);
  35. } else {
  36. addReplyLongLong(c, rank-1);
  37. }
  38. } else {
  39. addReply(c,shared.nullbulk);
  40. }
  41. }

zrem

  • 解释

从有序集合中删除指定元素,如果元素不存在,则忽略。
返回值:返回成功删除元素的给个数,如果元素不存在,返回0

  • 用法 zrem key member [member]
  • 示例

    127.0.0.1:6379> zadd key1 1 one
    (integer) 1
    127.0.0.1:6379> zadd key1 3 two
    (integer) 1
    127.0.0.1:6379> zrank key1 two
    1)1
    127.0.0.1:6379> zrem key1 one
    (integer)1

  • 源码

    / redis.c **/
    struct redisCommand readonlyCommandTable[] = {
    { “zrem”,zremCommand,3,0,NULL,1,1,1}
    }

    1.查找键是否存在并且是否是sortset数据类型

    1. 查找元素的对象,如果为null,返回0
    2. 从skiplist中删除

    void zremCommand(redisClient *c) {

    1. robj *zsetobj;
    2. zset *zs;
    3. dictEntry *de;
    4. double curscore;
    5. int deleted;
    6. //1.查找键是否存在并且是否是sortset数据类型
    7. if ((zsetobj = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
    8. checkType(c,zsetobj,REDIS_ZSET)) return;
    9. zs = zsetobj->ptr;
    10. c->argv[2] = tryObjectEncoding(c->argv[2]);
    11. //2. 查找元素的对象,如果为null,返回0
    12. de = dictFind(zs->dict,c->argv[2]);
    13. if (de == NULL) {
    14. addReply(c,shared.czero);
    15. return;
    16. }
    17. //3. 从skiplist中删除
    18. curscore = *(double*)dictGetEntryVal(de);
    19. deleted = zslDelete(zs->zsl,curscore,c->argv[2]);
    20. redisAssert(deleted != 0);
    21. /* Delete from the hash table */
    22. dictDelete(zs->dict,c->argv[2]);
    23. if (htNeedsResize(zs->dict)) dictResize(zs->dict);
    24. if (dictSize(zs->dict) == 0) dbDelete(c->db,c->argv[1]);
    25. touchWatchedKey(c->db,c->argv[1]);
    26. server.dirty++;
    27. addReply(c,shared.cone);

    }

zscore

  • 解释

    返回元素的分数,如果键或者元素不存在,返回nil

  • 用法 zscore key member
  • 示例

    127.0.0.1:6379> zadd key1 1 one
    (integer) 1
    127.0.0.1:6379> zadd key1 3 two
    (integer) 1
    127.0.0.1:6379> zscore key1 two
    “3”

  • 源码

    / redis.c **/
    struct redisCommand readonlyCommandTable[] = {
    { “zscore”,zscoreCommand,3,0,NULL,1,1,1}
    }

    1.查找键是否存在并且是否是sortset数据类型

    1. 查询元素对应的对象
    2. 如果元素不存在返回null
      4.元素存在,返回其分数

    void zscoreCommand(redisClient *c) {

    1. robj *o;
    2. zset *zs;
    3. dictEntry *de;
    4. //1.查找键是否存在并且是否是sortset数据类型
    5. if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
    6. checkType(c,o,REDIS_ZSET)) return;
    7. zs = o->ptr;
    8. c->argv[2] = tryObjectEncoding(c->argv[2]);
    9. //2. 查询元素对应的对象
    10. de = dictFind(zs->dict,c->argv[2]);
    11. if (!de) {
    12. //3. 如果元素不存在返回null
    13. addReply(c,shared.nullbulk);
    14. } else {
    15. //4.元素存在,返回其分数
    16. double *score = dictGetEntryVal(de);
    17. addReplyDouble(c,*score);
    18. }

    }

内部编码

skiplist 跳表

使用场景

场景一: 排行榜

用户为爱豆打榜,member存爱豆的id,用户每次打榜操作,增加爱豆id对应的score

场景二: 热搜话题热度

  1. 每个话题用户搜索,发布话题内容,对应的话题id的分数增加一定值。
  2. 每次返回前十个分数最高的话题

发表评论

表情:
评论列表 (有 0 条评论,35人围观)

还没有评论,来说两句吧...

相关阅读