集合(set) 类型也是用来保存多个的字符串元素, 但和列表类型不一样的是, 集合中不允许有重复元素, 并且集合中的元素是无序的, 不能通过
索引下标获取元素。
Redis除了支持集合内的增删改查, 同时还支持多个集合取交集、 并集、 差集, 合理地使用好集合类型, 能在实际开发中解决很多实际问题。
1 命令
1.集合内操作
(1) 添加元素
sadd key element [element ...]
127.0.0.1:6379> exists myset
(integer) 0
127.0.0.1:6379> sadd myset a b c
(integer) 3
127.0.0.1:6379> sadd myset a b
(integer) 0
(2) 删除元素
srem key element [element ...]
返回结果为成功删除元素个数, 例如:
127.0.0.1:6379> srem myset a b
(integer) 2
127.0.0.1:6379> srem myset hello
(integer) 0
(3) 计算元素个数
scard key
scard的时间复杂度为O(1) , 它不会遍历集合所有元素, 而是直接用
Redis内部的变量, 例如:
127.0.0.1:6379> scard myset
(integer) 1
(4) 判断元素是否在集合中
sismember key element
如果给定元素element在集合内返回1, 反之返回0, 例如:
127.0.0.1:6379> sismember myset c
(integer) 1
(5) 随机从集合返回指定个数元素
srandmember key [count]
127.0.0.1:6379> srandmember myset 2
1) "a"
2) "c"
127.0.0.1:6379> srandmember myset
"d"
(6) 从集合随机弹出元素
spop key
127.0.0.1:6379> spop myset
"c"
127.0.0.1:6379> smembers myset
1) "d"
2) "b"
3) "a"
需要注意的是Redis从3.2版本开始, spop也支持[count]参数。
srandmember和spop都是随机从集合选出元素, 两者不同的是spop命令执行后, 元素会从集合中删除, 而srandmember不会。
(7) 获取所有元素
smembers key
下面代码获取集合myset所有元素, 并且返回结果是无序的:
127.0.0.1:6379> smembers myset
1) "d"
2) "b"
3) "a"
smembers和lrange、 hgetall都属于比较重的命令, 如果元素过多存在阻塞Redis的可能性, 这时候可以使用sscan来完成。
2.集合间操作
现在有两个集合, 它们分别是user: 1: follow和user: 2: follow:
127.0.0.1:6379> sadd user:1:follow it music his sports
(integer) 4
127.0.0.1:6379> sadd user:2:follow it news ent sports
(integer) 4
(1) 求多个集合的交集
sinter key [key ...]
127.0.0.1:6379> sinter user:1:follow user:2:follow
1) "sports"
2) "it"
(2) 求多个集合的并集
suinon key [key ...]
127.0.0.1:6379> sunion user:1:follow user:2:follow
1) "sports"
2) "it"
3) "his"
4) "news"
5) "music"
6) "ent"
(3) 求多个集合的差集
sdiff key [key ...]
127.0.0.1:6379> sdiff user:1:follow user:2:follow
1) "music"
2) "his"
(4) 将交集、 并集、 差集的结果保存
sinterstore destination key [key ...]
suionstore destination key [key ...]
sdiffstore destination key [key ...]
127.0.0.1:6379> sinterstore user:1_2:inter user:1:follow user:2:follow
(integer) 2
127.0.0.1:6379> type user:1_2:inter
set
127.0.0.1:6379> smembers user:1_2:inter
1) "it"
2) "sports"
2 内部编码
集合类型的内部编码有两种:
·intset(整数集合) : 当集合中的元素都是整数且元素个数小于set-maxintset-entries配置(默认512个) 时, Redis会选用intset来作为集合的内部实
现, 从而减少内存的使用。
·hashtable(哈希表) : 当集合类型无法满足intset的条件时, Redis会使用hashtable作为集合的内部实现。
1) 当元素个数较少且都为整数时, 内部编码为intset:
127.0.0.1:6379> sadd setkey 1 2 3 4
(integer) 4
127.0.0.1:6379> object encoding setkey
"intset"
2.1) 当元素个数超过512个, 内部编码变为hashtable:
127.0.0.1:6379> sadd setkey 1 2 3 4 5 6 ... 512 513
(integer) 509
127.0.0.1:6379> scard setkey
(integer) 513
127.0.0.1:6379> object encoding listkey
"hashtable"
2.2) 当某个元素不为整数时, 内部编码也会变为hashtable:
127.0.0.1:6379> sadd setkey a
(integer) 1
127.0.0.1:6379> object encoding setkey
"hashtable"
3 使用场景
集合类型比较典型的使用场景是标签(tag) 。 例如一个用户可能对娱乐、 体育比较感兴趣, 另一个用户可能对历史、 新闻比较感兴趣, 这些兴趣
点就是标签。 有了这些数据就可以得到喜欢同一个标签的人, 以及用户的共同喜好的标签, 这些数据对于用户体验以及增强用户黏度比较重要。
下面使用集合类型实现标签功能的若干功能。
(1) 给用户添加标签
sadd user:1:tags tag1 tag2 tag5
sadd user:2:tags tag2 tag3 tag5
...
sadd user:k:tags tag1 tag2 tag4
...
(2) 给标签添加用户
sadd tag1:users user:1 user:3
sadd tag2:users user:1 user:2 user:3
...
sadd tagk:users user:1 user:2
...
(3) 删除用户下的标签
srem user:1:tags tag1 tag5
...
(4) 删除标签下的用户
srem tag1:users user:1
srem tag5:users user:1
...
开发提示
前面只是给出了使用Redis集合类型实现标签的基本思路, 实际上一个标签系统远比这个要复杂得多, 不过集合类型的应用场景通常为以下几种:
·sadd=Tagging(标签)
·spop/srandmember=Random item(生成随机数, 比如抽奖)
·sadd+sinter=Social Graph(社交需求)
备注:文章参考《Redis开发与运维》,作者:付磊,张益军