Redis的set和zset类及主要指令
文章目录Redis的set和zset类及主要指令一、Set集合1. Set的特点2. 内部实现3. 常用命令3.1 SADD3.2 SREM3.3 SMEMBERS3.4 SISMEMBER3.5 SCARD3.6 SPOP3.7 SRANDMEMBER3.8 SMOVE3.9 SINTER3.10 SUNION3.11 SDIFF3.12 交集/并集/差集的 STORE 版本3.13 SSCAN二、ZSet有序集合1. ZSet的特点2. 内部实现3. 常用命令3.1 ZADD3.2 ZREM3.3 ZSCORE3.4 ZRANK / ZREVRANK3.5 ZCARD3.6 ZRANGE3.7 ZREVRANGE仍可用建议用 ZRANGE ... REV 代替3.8 ZRANGEBYSCORE / ZREVRANGEBYSCORE3.9 ZINCRBY3.10 ZCOUNT3.11 ZREMRANGEBYRANK3.12 ZREMRANGEBYSCORE3.13 ZPOPMIN / ZPOPMAX3.14 ZSCAN3.15 ZINTER / ZUNIONRedis 6.2三、小结Set 和 ZSet 的核心差异Redis的set和zset类及主要指令一、Set集合1. Set的特点无序集合里的元素没有先后顺序不能通过下标访问唯一集合里的元素不会重复同一个元素插多次也只保留一份支持集合间的交、并、差运算适合做共同好友、标签聚合这类场景2. 内部实现编码说明intset整数集合元素全是整数且数量较少时使用内存连续查找用二分O(log n)hashtable哈希表元素不全是整数或数量超过阈值时使用也就是只用键、值都设为 NULL 的哈希表什么时候 intset 会转成 hashtable插入了非整数的元素元素数量超过set-max-intset-entries默认 512这个转换是单向的就算后来把非整数元素删了也不会再降回 intset。所以如果确认集合里全是整数且数量可控intset 能省不少内存。3. 常用命令3.1 SADD往集合里添加元素。已经存在的元素会被忽略不会报错。语法SADD key member [member ...]返回值实际新增的元素个数已存在的元素不算示例redis SADD colors red (integer) 1 redis SADD colors blue green (integer) 2 redis SADD colors red (integer) 0 redis SMEMBERS colors 1) blue 2) green 3) red注意返回值的含义第一次 SADD “red” 返回 1因为确实新增了第二次再 SADD “red” 返回 0说明它已经在集合里了不是报错只是没干事。3.2 SREM从集合里移除元素。元素不存在也不会报错。语法SREM key member [member ...]返回值实际移除的元素个数不存在的元素不算示例redis SADD colors red blue green (integer) 3 redis SREM colors blue yellow (integer) 1 redis SMEMBERS colors 1) green 2) red上面 “yellow” 本来就不在集合里所以 SREM 返回 1只删了 “blue”不是 2。3.3 SMEMBERS返回集合里所有的元素。注意集合本身是无序的所以每次返回的顺序不一定一样。语法SMEMBERS key返回值所有元素的列表key 不存在时返回空列表示例redis SADD colors red blue green (integer) 3 redis SMEMBERS colors 1) blue 2) green 3) red**⚠️ 注意**SMEMBERS 跟 hash 里的 HGETALL 一样属于重操作。如果集合里有几十上百万个元素一次性全拉出来会阻塞 Redis。大集合遍历请用 SSCAN 分批次拿。3.4 SISMEMBER判断某个元素是不是在集合里。语法SISMEMBER key member返回值1 表示存在0 表示不存在包括 key 不存在的情况示例redis SADD colors red blue (integer) 2 redis SISMEMBER colors red (integer) 1 redis SISMEMBER colors green (integer) 0 redis SISMEMBER nonexist red (integer) 03.5 SCARD返回集合里的元素个数。相当于集合版的 LLEN。语法SCARD key返回值集合的基数元素数量key 不存在时返回 0示例redis SADD colors red blue green (integer) 3 redis SCARD colors (integer) 3 redis SCARD nonexist (integer) 03.6 SPOP随机从集合里弹出一个或多个元素弹出后会从集合中删除。这跟 list 的 LPOP/RPOP 不一样——list 是从固定方向弹set 是随机弹。语法SPOP key [count]返回值弹出的元素或元素列表key 不存在 / 空集合返回 nil示例redis SADD colors red blue green yellow (integer) 4 redis SPOP colors blue redis SPOP colors 2 1) yellow 2) red redis SMEMBERS colors 1) greenRedis 3.2 之后支持传 count 参数一次随机弹出多个。count 不能为负数。3.7 SRANDMEMBER随机返回集合里的元素但不删除。SPOP 是拿走SRANDMEMBER 是看看。语法SRANDMEMBER key [count]返回值不传 count 返回一个元素传了返回列表示例redis SADD colors red blue green (integer) 3 redis SRANDMEMBER colors green redis SRANDMEMBER colors 2 1) blue 2) green redis SRANDMEMBER colors -3 1) green 2) green 3) bluecount 参数跟 HRANDFIELD 一样count 为正返回的元素不重复最多返回整个集合count 为负返回的元素可能重复凑够 |count| 个为止3.8 SMOVE把某个元素从源集合移动到目标集合。这是一个原子操作——要么两端都成功要么都不动。语法SMOVE source destination member返回值1 表示移动成功0 表示源集合里没有这个元素示例redis SADD set1 a b c (integer) 3 redis SADD set2 d e (integer) 2 redis SMOVE set1 set2 b (integer) 1 redis SMEMBERS set1 1) a 2) c redis SMEMBERS set2 1) b 2) d 3) e redis SMOVE set1 set2 x (integer) 0如果目标集合已经存在这个元素SMOVE 也只会把源集合里的删掉目标集合不会出现重复集合本来就不允许重复。3.9 SINTER求多个集合的交集返回同时出现在所有集合里的元素。语法SINTER key [key ...]返回值交集的元素列表示例redis SADD set1 a b c (integer) 3 redis SADD set2 b c d (integer) 3 redis SADD set3 b c e (integer) 3 redis SINTER set1 set2 set3 1) b 2) c3.10 SUNION求多个集合的并集把所有集合的元素合在一起自动去重。语法SUNION key [key ...]返回值并集的元素列表示例redis SADD set1 a b c (integer) 3 redis SADD set2 d e (integer) 2 redis SUNION set1 set2 1) a 2) b 3) c 4) d 5) e3.11 SDIFF求多个集合的差集返回在第一个集合里但不在后续集合里的元素。语法SDIFF key [key ...]返回值差集的元素列表示例redis SADD set1 a b c d (integer) 4 redis SADD set2 c d e (integer) 3 redis SDIFF set1 set2 1) a 2) b redis SDIFF set2 set1 1) e顺序很重要SDIFF A B和SDIFF B A结果完全不同前者是在 A 但不在 B后者是在 B 但不在 A。3.12 交集/并集/差集的 STORE 版本这三个集合运算都有对应的STORE版本差别是结果不直接返回而是存到 destination 里。适合中间结果要多次使用的场景不用每次都重新算。SINTERSTORE destination key [key ...] SUNIONSTORE destination key [key ...] SDIFFSTORE destination key [key ...]返回值结果集合的元素个数示例redis SADD set1 a b c (integer) 3 redis SADD set2 b c d (integer) 3 redis SINTERSTORE inter set1 set2 (integer) 2 redis SMEMBERS inter 1) b 2) c3.13 SSCAN分批次迭代集合里的元素是遍历大集合的正确姿势。用法跟 SCAN 一样。语法SSCAN key cursor [MATCH pattern] [COUNT count]返回值下一轮的游标和当前批次的元素列表。游标返回 0 时表示遍历结束。示例redis SADD colors red blue green yellow purple orange (integer) 6 redis SSCAN colors 0 MATCH *e* COUNT 2 1) 4 2) 1) purple 2) blue redis SSCAN colors 4 1) 0 2) 1) orange 2) green 3) yellow 4) red游标不一定连续不能用 for 循环递增——每次用上一轮返回的游标值就行0 代表结束。二、ZSet有序集合1. ZSet的特点跟 set 一样member 唯一不重复但每个 member 关联一个score分值元素按 score 从小到大排列score 可以重复——多个 member 可以有相同的分数这时候按 member 的字典序排适合排行榜、延迟队列、带权重的标签这类既要去重又要排序的场景2. 内部实现编码说明ziplist / listpack元素少且短时使用member 和 score 交替紧凑存放省内存skiplist跳表 hashtable数据量大时使用。跳表负责按 score 排序hashtable 负责 O(1) 查 member 的 score什么时候切换元素数量超过zset-max-ziplist-entries或zset-max-listpack-entries某个 member 的长度超过zset-max-ziplist-value或zset-max-listpack-value这个切换逻辑跟 hash 是一样的少量数据用紧凑结构省内存数据多了切跳表保证查询性能。为什么需要跳表 哈希表一起上跳表按 score 范围查很高效ZRANGE、ZRANGEBYSCORE但按 member 查 score 的时候得遍历O(log n) 虽然也不差但配合一个 hashtable 直接 O(1) 更划算。Redis 是拿空间换时间——反正 hashtable 只存 member→score 的指针实际数据不存两份开销没那么大。3. 常用命令3.1 ZADD往有序集合里添加 member 和 score。member 已存在时会更新 score。语法ZADD key [NX|XX] [GT|LT] [CH] [INCR] score member [score member ...]选项说明NX只在 member 不存在时才添加XX只在 member 存在时才更新GT只在新 score 旧 score 时才更新LT只在新 score 旧 score 时才更新CH返回值改成被修改的元素数新增 更新都算默认只统计新增INCR对 member 的 score 做增量操作此时只能操作一个 member返回值默认返回新增的 member 数加了 CH 返回变更数加了 INCR 返回新的 score示例redis ZADD rank 100 zhangsan 80 lisi 60 wangwu (integer) 3 redis ZADD rank 90 lisi (integer) 0 redis ZADD rank CH 200 zhangsan (integer) 1 redis ZADD rank NX 70 lisi (integer) 0第一个 ZADD 返回 3三个都是新增。第二个 ZADD 更新了 lisi 的 score但只算新增所以返回 0。第三个加了 CH 参数“zhangsan” 的 score 被更新了算一次变更。第四个 NX 了 “lisi”但它已经存在不执行返回 0。3.2 ZREM从有序集合里删除 member。语法ZREM key member [member ...]返回值实际删除的 member 数示例redis ZADD rank 100 zhangsan 80 lisi 60 wangwu (integer) 3 redis ZREM rank lisi zhaoliu (integer) 1 redis ZRANGE rank 0 -1 1) wangwu 2) zhangsan3.3 ZSCORE返回某个 member 的 score。member 不存在返回 nil。语法ZSCORE key member返回值score 的值字符串格式的浮点数member 不存在返回 nil示例redis ZADD rank 100 zhangsan 80 lisi (integer) 2 redis ZSCORE rank zhangsan 100 redis ZSCORE rank wangwu (nil)3.4 ZRANK / ZREVRANKZRANK 返回 member 在有序集合中的升序排名从 0 开始score 最小的排第 0。ZREVRANK 相反按 score 降序排名score 最大的排第 0。语法ZRANK key member ZREVRANK key member返回值排名member 不存在返回 nil示例redis ZADD rank 100 zhangsan 80 lisi 60 wangwu (integer) 3 redis ZRANK rank lisi (integer) 1 redis ZREVRANK rank lisi (integer) 1“lisi” 的 score80升序排第二0: wangwu/60, 1: lisi/80, 2: zhangsan/100降序也排第二0: zhangsan/100, 1: lisi/80, 2: wangwu/60。3.5 ZCARD返回有序集合里的元素个数。语法ZCARD key返回值member 数量key 不存在返回 0示例redis ZADD rank 100 zhangsan 80 lisi 60 wangwu (integer) 3 redis ZCARD rank (integer) 33.6 ZRANGE按 score 升序返回指定下标范围的 member。默认只返回 member加 WITHSCORES 同时返回 score。语法ZRANGE key start stop [BYSCORE | BYLEX] [REV] [LIMIT offset count] [WITHSCORES]返回值member 的列表带 WITHSCORES 时 member、score 交替示例redis ZADD rank 100 zhangsan 80 lisi 60 wangwu (integer) 3 redis ZRANGE rank 0 -1 1) wangwu 2) lisi 3) zhangsan redis ZRANGE rank 0 -1 WITHSCORES 1) wangwu 2) 60 3) lisi 4) 80 5) zhangsan 6) 100 redis ZRANGE rank 0 0 1) wangwuRedis 6.2 之后 ZRANGE 功能更丰富了加了 REV 的效果跟 ZREVRANGE 一样加了 BYSCORE 就跟 ZRANGEBYSCORE 一样加了 BYLEX 就跟 ZRANGEBYLEX 一样。不过为了兼容老代码旧的 ZREVRANGE、ZRANGEBYSCORE 这些命令依然能用。3.7 ZREVRANGE仍可用建议用 ZRANGE … REV 代替按 score 降序返回指定下标范围的 member。语法ZREVRANGE key start stop [WITHSCORES]返回值member 列表示例redis ZADD rank 100 zhangsan 80 lisi 60 wangwu (integer) 3 redis ZREVRANGE rank 0 -1 WITHSCORES 1) zhangsan 2) 100 3) lisi 4) 80 5) wangwu 6) 603.8 ZRANGEBYSCORE / ZREVRANGEBYSCORE按 score 范围过滤 member。ZRANGEBYSCORE 是升序ZREVRANGEBYSCORE 是降序。支持开闭区间。语法ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count] ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count](表示开区间(80表示 score 80不包括 80-inf和inf表示负无穷和正无穷ZREVRANGEBYSCORE 写的是max在前min在后因为它从大到小排返回值满足条件的 member 列表示例redis ZADD rank 100 zhangsan 80 lisi 60 wangwu 90 zhaoliu (integer) 4 redis ZRANGEBYSCORE rank 70 100 1) lisi 2) zhaoliu 3) zhangsan redis ZRANGEBYSCORE rank (80 100 1) zhaoliu 2) zhangsan redis ZRANGEBYSCORE rank -inf inf 1) wangwu 2) lisi 3) zhaoliu 4) zhangsan redis ZRANGEBYSCORE rank 70 100 LIMIT 1 2 1) zhaoliu 2) zhangsan redis ZREVRANGEBYSCORE rank 100 70 1) zhangsan 2) zhaoliu 3) lisi3.9 ZINCRBY把 member 的 score 加上一个增量。member 不存在就当它 score 为 0 再加。语法ZINCRBY key increment member返回值加完后的 score示例redis ZADD rank 100 zhangsan 80 lisi (integer) 2 redis ZINCRBY rank 10 lisi 90 redis ZINCRBY rank 200 wangwu 200 redis ZINCRBY rank -15 zhangsan 85排行榜场景里 ZINCRBY 很实用——用户得分变化时直接调用就完了不用先 ZSCORE 拿到旧值再加回去一步到位。3.10 ZCOUNT统计 score 在指定区间里的 member 个数。语法ZCOUNT key min max返回值符合条件的 member 数量示例redis ZADD rank 100 zhangsan 80 lisi 60 wangwu 90 zhaoliu (integer) 4 redis ZCOUNT rank 70 100 (integer) 3 redis ZCOUNT rank (80 100 (integer) 23.11 ZREMRANGEBYRANK按排名范围删除 member。下标从 0 开始。语法ZREMRANGEBYRANK key start stop返回值删除的 member 个数示例redis ZADD rank 100 zhangsan 80 lisi 60 wangwu 90 zhaoliu (integer) 4 redis ZREMRANGEBYRANK rank 0 1 (integer) 2 redis ZRANGE rank 0 -1 1) zhaoliu 2) zhangsan3.12 ZREMRANGEBYSCORE按 score 范围删除 member。语法ZREMRANGEBYSCORE key min max返回值删除的 member 个数示例redis ZADD rank 100 zhangsan 80 lisi 60 wangwu 90 zhaoliu (integer) 4 redis ZREMRANGEBYSCORE rank 60 80 (integer) 2 redis ZRANGE rank 0 -1 1) zhaoliu 2) zhangsan3.13 ZPOPMIN / ZPOPMAX弹出 score 最小或最大的 member。可以一次弹出多个。语法ZPOPMIN key [count] ZPOPMAX key [count]返回值弹出 member 和 score 的交替列表示例redis ZADD rank 100 zhangsan 80 lisi 60 wangwu (integer) 3 redis ZPOPMIN rank 1) wangwu 2) 60 redis ZPOPMAX rank 2 1) zhangsan 2) 100 3) lisi 4) 80ZPOPMIN/ZPOPMAX 在 Redis 5.0 引入。做简单的延迟队列或者任务调度时很好用——比如用 score 存时间戳ZPOPMIN 弹出最早该执行的任务。3.14 ZSCAN跟 SSCAN 一样分批次迭代有序集合。返回 member 和 score 交替排列。语法ZSCAN key cursor [MATCH pattern] [COUNT count]返回值下一轮游标和当前批次的 member-score 列表示例redis ZADD rank 100 zhangsan 80 lisi 60 wangwu (integer) 3 redis ZSCAN rank 0 1) 0 2) 1) wangwu 2) 60 3) lisi 4) 80 5) zhangsan 6) 1003.15 ZINTER / ZUNIONRedis 6.2有序集合的交集和并集。比直接使用 ZINTERSTORE/ZUNIONSTORE 多了直接返回结果的选项。语法ZINTER numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX] [WITHSCORES] ZUNION numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX] [WITHSCORES]参数说明WEIGHTS每个集合的权重做运算前会把 score 先乘上对应权重AGGREGATE多个集合里相同 member 的 score 怎么合并。SUM 求和默认、MIN 取最小、MAX 取最大示例redis ZADD z1 10 a 20 b (integer) 2 redis ZADD z2 5 a 15 c (integer) 2 redis ZINTER 2 z1 z2 1) a redis ZINTER 2 z1 z2 WITHSCORES 1) a 2) 15 redis ZINTER 2 z1 z2 WEIGHTS 2 3 WITHSCORES 1) a 2) 35 redis ZUNION 2 z1 z2 AGGREGATE MAX WITHSCORES 1) a 2) 10 3) b 4) 20 5) c 6) 15上面 ZINTER 里 “a” 的 score 是 15因为默认 AGGREGATE 是 SUM10 × 1 5 × 1 15。加了 WEIGHTS 2 3 之后10 × 2 5 × 3 35。ZUNION 跟的是 MAX 策略各 member 取最大的 score。三、小结Set 和 ZSet 的核心差异维度Set集合ZSet有序集合有没有顺序无序遍历顺序不稳定按 score 升序排列score 相同按字典序元素组成只有 membermember scorescore 是双精度浮点数唯一性member 唯一member 唯一但 score 可以重复内部编码intset / hashtablelistpack(ziplist) / skiplist hashtable典型场景标签、共同好友、去重、随机抽奖排行榜、延迟队列、按时间排序的 feed核心操作交/并/差集SINTER/SUNION/SDIFF按 score 范围查ZRANGEBYSCORE、按排名查ZRANK内存占用相对小纯整数场景下 intset 非常省稍大跳表指针 hashtable 有额外开销总结如果只需要有没有去重、标签、交集用 Set如果还需要谁排前面排行榜、优先级、时间线用 ZSet。两者的选择说到底就是一个问题要不要排序。不要排序就 Set要排序就 ZSet。ZSet 多出来的那个 score本质上就是排序用的权重没了它 ZSet 跟 Set 做的事差不多。