Redis 4.x系列(四):Redis 数据类型之Set、Sorted Set

Set集合类型,是一个无序的,不可重复的字符串集合,可以快速测试成员在集合中是否存在,重复项删除和集合运算(求并、交、差集) 。Set 集合中最大成员数量为 2 的 32 次方减 1(4294967295)

Set 集合

Set 集合的不可重复特性,常用来跟踪具有唯一性的事物。例如要统计访问网站的用户数,可以根据用户 IP 地址来统计,只需要在有访问时就把 IP 加入到 Set 集合,集合中不会出现重复的 IP;计算水果种类,只需把水果命称加入到集合中而不需要判断是否重复, 还可用于存储中奖用户的 ID,因为有去重功能,可以保证同一用户不会中奖两次。

内部编码

Redis 内部使用两种编码方式来存储对集合对象:

  • intset:元素都是整数,且元素个数小于配置中 set-max-intset-entries 选项设置的值(默认 512)的集合,采用此编码,对于较小的集合可以节省占用空间。
  • hashtable:intset 不适用时的默认编码。

常用命令

  • sadd:添加元素到集合。在添加时,如果键不存在,Redis 则创建一个空键集合;也会自动删除空集合对应的键。
  • srem:从集合中删除元素。
  • sismember:查看元素是否在集合中,存在返回 1,不存在返回 0。
  • smembers:列出所有成员。该命令在大集合中使用可能会阻塞服务器,因此推荐使用sscan命令。
  • scard:获取集合中成员数量。
  • sscan:基于指针的增量迭代遍历元素,不会造成服务器阻塞;在使用时需要指定一个游标(从 0 开始), 返回一个列表以及一个新游标,新游标可用于下一次迭代。
  • srandmember key [count]:随机返回指定个数无素。count可选,如果不写默认为1
  • spop key [count]:从集合中随机弹出指定个数元素。可用于生成随机数,比如抽奖。

Set常用的使用场景:标签系统,用户有多个爱好标签,一个标签有多个用户,在社交应用,推荐系统中常用。

sadd:添加

添加元素到有序集合

语法:sadd key value [value.....]
示例:

1
2
3
# 添加元素
127.0.0.1:6379> sadd fruit apple orange banana pear peach watermelon
(integer) 6

srm:删除

删除元素

语法:srm key value
示例:

1
2
3
4
5
# 删除
127.0.0.1:6379> srem fruit apple
(integer) 1
127.0.0.1:6379> scard fruit
(integer) 5

scard:查个数

查看集合元素个数

语法:scard key
示例:

1
2
3
# 统计元素
127.0.0.1:6379> scard fruit
(integer) 6

sismember:是否存在

查看元素在集合中是否存在,存在返回 1, 不存在返回 0

语法:sismember key value
示例:

1
2
3
4
5
6
7
8
9
# 存在
127.0.0.1:6379> sismember fruit apple
(integer) 1
127.0.0.1:6379> sismember fruit pear
(integer) 1

# 不存在
127.0.0.1:6379> sismember fruit graper
(integer) 0

smembers:列出所有成员

列出有序集合里的所有成员

语法:smembers key
示例:

1
2
3
4
5
6
7
# 列出所有成员
127.0.0.1:6379> smembers fruit
1) "watermelon"
2) "pear"
3) "orange"
4) "banana"
5) "peach"

sscan:根据游标查询

根据游标和匹配规则获取集合元素

语法:sscan key cursor [MATCH pattern] [COUNT number]
示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# sscan
127.0.0.1:6379> sscan fruit 0
1) "0"
2) 1) "watermelon"
2) "pear"
3) "orange"
4) "banana"
5) "peach"
127.0.0.1:6379> sscan fruit 0 match *
1) "0"
2) 1) "watermelon"
2) "pear"
3) "orange"
4) "banana"
5) "peach"
127.0.0.1:6379> sscan fruit 0 match *peach*
1) "0"
2) 1) "peach"
127.0.0.1:6379> sscan fruit 0 match *p*
1) "0"
2) 1) "pear"
2) "peach"

集合运算:交/并/差

Set 集合运算有求并集的sunionsunionstore命令,求交集的sintersinterstore命令,求差集的sdiffsdiffstore命令。其中不带store后缀的命令只返回相应操作的集合;带store后缀的命令则会将结果存储到指定的键中。

1
2
3
4
5
# 添加两个集合
127.0.0.1:6379> sadd shop1 apple orange banana pear peach watermelon
(integer) 6
127.0.0.1:6379> sadd shop2 orange pear watermelon peanut cherry pineapple
(integer) 6

求并集

命令:sunion/sunionstore

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
127.0.0.1:6379> sunion shop1 shop2
1) "cherry"
2) "watermelon"
3) "pear"
4) "banana"
5) "orange"
6) "peanut"
7) "peach"
8) "apple"
9) "pineapple"
127.0.0.1:6379> sunionstore union_shop shop1 shop2
(integer) 9
127.0.0.1:6379> smembers union_shop
1) "cherry"
2) "watermelon"
3) "pear"
4) "banana"
5) "orange"
6) "peanut"
7) "peach"
8) "apple"
9) "pineapple"

求交集

命令:sinter/sinterstore

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
127.0.0.1:6379> sinter "shop1" "shop2"
1) "orange"
2) "watermelon"
3) "pear"
127.0.0.1:6379> sinterstore "shop3" "shop1" "shop2"
(integer) 3
127.0.0.1:6379> smembers shop3
1) "pear"
2) "watermelon"
3) "orange"
127.0.0.1:6379> sscan shop3 0
1) "0"
2) 1) "pear"
2) "watermelon"
3) "orange"
127.0.0.1:6379>

求差集

命令:sdiff / sdiffstore

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
127.0.0.1:6379> sdiff shop1 shop2
1) "banana"
2) "peach"
3) "apple"
127.0.0.1:6379> sdiffstore diff_shop shop1 shop2
(integer) 3
127.0.0.1:6379> smembers diff_shop
1) "banana"
2) "peach"
3) "apple"
127.0.0.1:6379> sscan diff_shop 0
1) "0"
2) 1) "banana"
2) "peach"
3) "apple"

Sorted Set

Sorted set 是一个有序集合,不可重复集合,与 Set 集合非常类似。有序集合的每个元素都有一个用于排序的权重,权重分可重复。

使用有序集合,可以快速地对集合中的元素执行添加、删除、更新操作,可以按顺序从集合中获取元素。有序集合在某些需要一直保持数据有序的场景中非常有用,例如排行榜类场景。

内部编码

Redi 内部使用两种编码方式存储有序集合对象:

  1. **ziplist(压缩列表)**:对于元素个数小于配置中 zset-max-ziplist-entries 选项配置的值(默认 128 ),且每个元素的大小都小于配置中 zset-max-ziplist-value 选项配置的值(默认为 64 字节)的有序集合,采用此编码。 ziplist 用于节省较小集合所占用的空间。
  2. **skiplist(跳跃表)**:当 ziplist 不适用时使用的默认编码。

常用命令

  • zadd key member:添加元素。
  • zrem key member:删除元素。
  • zremrangebyrank key start end:删除指定排名内的升序元素。
  • zremrangebyscore key min max:删除指定分数范围的元素。
  • zcard:计算成员个数。
  • zrange:根据索引范围获取集合元素,按权重分从小到大排序。
  • zrevrange:根据索引范围获取集合元素,按权重分从大到小排序。
  • zrangebyscore key min max [withscores] [limit offset count]:按照分数从低到高返回指定范围的成员。
  • zrevrangebyscore key min max [withscores] [limit offset count]:按照分数从高到低返回指定范围的成员。
  • zincr:对 score 做增加。
  • zincrby:修改元素权重分。
  • zrank:是从分数从低到高返回排名。
  • zrevrank:获取元素排名所在索引位置,第一位索位 0 。
  • zscore:获取元素的权重分(投票数),不存在返回 nil
  • zinterstore:获取有序集合的交集,并存到到新的有集集合中,并可以指定元素权重分,默认情况下,元素的结果分数是其存在的有序集合中的分数之和。
  • zunionstore:将两个有序集合的并集保存到指定键中(创建一个新的有序集合保存并集),且可以指定各个有序集合的不同权重分。
  • zcount key min max:获取指定分数范围的元素个数。

zadd:添加

语法:zadd key [NX|XX] [CH] [INCR] score member [score member ...]

  • NX在成员不存在时添加成员;
  • XX 表示更新已存在的的成员,而不是添加新成员;
  • CH 返回此次操作后,有序集合元素和分数发生变化的个数。

zadd添加新的成员,允许成员具有相同的权重,Redis 将按照字典顺序进行排序。
示例:

1
2
3
127.0.0.1:6379> zadd ranking:film 1431 gold_brother 690 fantanfengbao 516 diezhongdie3 293 jianghuernv
(integer) 4
```

zrange:根据索引范围获取

语法:zrange key start stop [WITHSCORES], 根据索引范围获取集合元素,按权重分从小到大排序。
示例:

1
2
3
4
5
6
7
8
9
127.0.0.1:6379> zrange ranking:film 0 -1 withscores
1) "jianghuernv"
2) "293"
3) "diezhongdie3"
4) "516"
5) "fantanfengbao"
6) "690"
7) "gold_brother"
8) "1431"

ZRANGEBYSCORE

语法:ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count],返回有序集合中指定分数区间内的成员,权重分由小到大排序, 支持分页。
示例:

1
2
3
4
5
127.0.0.1:6379> ZRANGEBYSCORE ranking:film 516 690 withscores 
1) "diezhongdie3"
2) "516"
3) "fantanfengbao"
4) "690"

ZRANGEBYSCORE还可用于实现延时队列,使用时间戳作为 score,消息内容作为 member 调用 zadd 生产消息,消费者使用ZRANGEBYSCORE指令获取** N **秒之前的数据轮询进行处理。

zrevrange

语法:zrevrange key start stop [WITHSCORES], 获取索引范围内的元素,权重分由大到小排序。
示例:

1
2
3
4
5
6
7
8
9
127.0.0.1:6379> zrevrange ranking:film 0 -1 withscores
1) "gold_brother"
2) "1431"
3) "fantanfengbao"
4) "690"
5) "diezhongdie3"
6) "516"
7) "jianghuernv"
8) "293"

zrevrangebyscore

语法:zrevrangebyscore key max min [WITHSCORES] [LIMIT offset count],返回有序集合中指定分数区间内的成员,权重分由大到小排序, 支持分页。min 和 max 还支持开区间(小括号)和闭区间(中括号),**-inf** 和 +inf分别代表无限小和无限大。
示例:

1
2
3
4
5
6
7
127.0.0.1:6379> zrevrangebyscore ranking:film 690 293 withscores
1) "fantanfengbao"
2) "690"
3) "diezhongdie3"
4) "516"
5) "jianghuernv"
6) "293"

zcard:查个数

语法:zcard key, 获取有序集合中元素个数。
示例:

1
2
127.0.0.1:6379> zcard ranking:film
(integer) 4

zincrby:改权重分

语法:zincrby key increment member, 修改元素权重(投票数)
示例:

1
2
3
4
5
6
7
8
9
10
11
127.0.0.1:6379> zincrby ranking:film 300 jianghuernv
"593"
127.0.0.1:6379> zrevrange ranking:film 0 -1 withscores
1) "gold_brother"
2) "1431"
3) "fantanfengbao"
4) "690"
5) "jianghuernv"
6) "593"
7) "diezhongdie3"
8) "516"

zrevrank:获取元素排名

语法:zrevrank key member, 获取元素在集合中的排名(索引),起始位是 0。
示例:

1
2
3
4
127.0.0.1:6379> zrevrank ranking:film fantanfengbao
(integer) 1
127.0.0.1:6379> zrevrank ranking:film gold_brother
(integer) 0

zscore:获取权重分

语法:zscore key member, 获取元素的权重分(投票数)
示例:

1
2
127.0.0.1:6379> zscore ranking:film fantanfengbao
"690"

zrem:删除元素

语法:zrem key member [member ...],删除集合中的元素成员
示例:

1
2
3
4
5
6
127.0.0.1:6379> zcard ranking:film
(integer) 4
127.0.0.1:6379> zrem ranking:film jianghuernv
(integer) 1
127.0.0.1:6379> zcard ranking:film
(integer) 3

集合运算:交/并

示例:新增两个集合以备运算

1
2
3
4
5
6
7
8
9
10
11
12
13
# 添加一个新集合
127.0.0.1:6379> zadd ranking:film1 98 hudanzhuixiong 19 danaoxiyou 8 xihongshishoufu 9 juchisha 11 aimaozhicheng
(integer) 5
# 查看
127.0.0.1:6379> zrevrange ranking:film 0 -1 withscores
1) "gold_brother"
2) "1431"
3) "fantanfengbao"
4) "690"
5) "jianghuernv"
6) "593"
7) "diezhongdie3"
8) "516"

zunionstore:并集

合并两个集合的元素到一个新的有序集合。

语法:zunionstore destination numkeys key [key ...] [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX]

  • destination:计算结果保存到这个键
  • numkeys:需要计算的键的个数
  • key [key …]:需要做计算的键
  • weights weight [weight …]:每个键的权重,在计算时,每个成员的分数乘以这个键权重,每个键权重默认
  • aggregate sum|min|max:计算成员后,分值可根据此设置项进行统计,默认是 sum

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 合并
127.0.0.1:6379> zunionstore totalfilm 2 ranking:film ranking:film1 weights 1 2
(integer) 9

# 查看合并结果
127.0.0.1:6379> zrevrange totalfilm 0 -1 withscores
1) "gold_brother"
2) "1431"
3) "fantanfengbao"
4) "690"
5) "jianghuernv"
6) "593"
7) "diezhongdie3"
8) "516"
9) "hudanzhuixiong"
10) "196"
11) "danaoxiyou"
12) "38"
13) "aimaozhicheng"
14) "22"
15) "juchisha"
16) "18"
17) "xihongshishoufu"
18) "16"

zinterstore:交集

求两个集合的交集,存储到一个新的有序集合中。

语法:zinterstore destination numkeys key [key ...] [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX]
示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
127.0.0.1:6379> ZADD zset1 1 "one"
(integer) 1
127.0.0.1:6379> ZADD zset1 2 "two"
(integer) 1
127.0.0.1:6379> ZADD zset2 1 "one"
(integer) 1
127.0.0.1:6379> ZADD zset2 2 "two"
(integer) 1
127.0.0.1:6379> ZADD zset2 3 "three"
(integer) 1
127.0.0.1:6379> zinterstore out0 2 zset1 zset2
(integer) 2
127.0.0.1:6379> zrange out0 0 -1 withscores
1) "one"
2) "2"
3) "two"
4) "4"
127.0.0.1:6379> zrevrange out0 0 -1 withscores
1) "two"
2) "4"
3) "one"
4) "2"
127.0.0.1:6379> zinterstore out3 2 zset1 zset2 weights 2 3 aggregate sum
(integer) 2
127.0.0.1:6379> zrevrange out3 0 -1 withscores
1) "two"
2) "10"
3) "one"
4) "5"
127.0.0.1:6379> zinterstore out4 2 zset1 zset2 weights 1 2 aggregate sum
(integer) 2
127.0.0.1:6379> zrevrange out4 0 -1 withscores
1) "two"
2) "6"
3) "one"
4) "3"
127.0.0.1:6379> zinterstore out5 2 zset1 zset2 weights 1 2 aggregate min
(integer) 2
127.0.0.1:6379> zrevrange out5 0 -1 withscores
1) "two"
2) "2"
3) "one"
4) "1"
127.0.0.1:6379> zinterstore out6 2 zset1 zset2 weights 1 2 aggregate max
(integer) 2
127.0.0.1:6379> zrevrange out6 0 -1 withscores
1) "two"
2) "4"
3) "one"
4) "2"

相关参考

Redis 命令参考

Redis 4.x系列(四):Redis 数据类型之Set、Sorted Set

http://blog.gxitsky.com/2018/09/25/Redis-4-datatype-2-set-sorted_set/

作者

光星

发布于

2018-09-25

更新于

2022-08-14

许可协议

评论