Redis数据类型-集合对象

  • 集合对象
  • 集合对象的编码转换
  • 集合对象的命令(包括不同编码情况下的实现方法)
  • 集合对象相关命令
  • 集合内操作
  • 1.添加元素-sadd
  • 2.删除元素-srem
  • 3.将元素从一个集合移动到另一个集合-smove
  • 4.计算元素个数-scard
  • 5.判断元素是否在集合中-sismember
  • 6.随机从集合内 返回 指定个数 元素-srandmember
  • 7.从集合内随机弹出(删除)元素-spop
  • 8.获取所有元素-smembers
  • 集合间操作
  • 1.求多个集合的交集-sinter
  • 2.求多个集合的并集-suinon
  • 3.求多个集合的差集-sdiff
  • 4.将交集、并集、差集的结果保存
  • 时间复杂度
  • 集合对象的使用场景(重要!!!)
  • 总结(重要!!!)


集合对象

集合(set)类型也是用来保存多个的字符串元素
但和列表类型不一样的是,集合中不允许有重复元素,并且集合中的元素是无序的,不能通过索引下标获取元素

集合和列表这两个区别带来的差异主要跟命令的复杂度有关:

  • 在执行像linsert和lrem这样的列表命令时,即使命令只针对单个列表元素,程序有时也不得不遍历整个列表以确定指定的元素是否存在,因此这些命令的复杂度都为O(N)
  • 对于集合来说,因为所有针对单个元素的集合命令都不需要遍历整个集合,所以复杂度都为O(1)

因此当需要存储多个元素时,就可以考虑这些元素是否可以以无序的方式存储,并且是否不会出现重复,如果是,那么就可以使用集合来存储这些元素,从而有效地利用集合操作的效率优势

一个集合最多可以存储232-1个元素
Redis除了支持集合内的增删改查,同时还支持多个集合取交集、并集、差集,合理地使用好集合类型

集合对象的编码可以是intset或者hashtable 阅读参考:Redis数据结构(五)-整数集合(intset)
intset编码 的 集合对象 使用 整数集合 作为 底层实现,集合对象 包含的所有元素 都被保存在 整数集合里面

举个例子,以下代码将创建一个intset编码集合对象

redis> SADD numbers 1 3 5
(integer) 3

redis整数集合 redis存集合对象_inset

另一方面,hashtable编码的集合对象使用字典作为底层实现,字典的每个键 都是一个 字符串对象,每个字符串对象 包含了一个 集合元素,而字典的值 则 全部被设置为 NULL

举个例子,以下代码将创建一个hashtable编码集合对象

redis整数集合 redis存集合对象_inset_02

集合对象的编码转换

集合对象可以同时满足以下两个条件时,对象使用intset编码

  • 集合对象保存的所有元素都是整数值
  • 集合对象保存的元素数量不超过512个

不能满足这两个条件的集合对象需要使用hashtable编码

注意:
第二个条件的上限值是可以修改的,具体请看配置文件中关于set-max-intset-entries选项的说明
对于使用intset编码的集合对象来说,当使用intset编码所需的两个条件的任意一个不能被满足时,就会执行对象的编码转换操作,原本保存在整数集合中的所有元素都会被转移并保存到字典里面,并且对象的编码也会从intset变为hashtable

举个例子,以下代码创建了一个只包含整数元素的集合对象,该对象的编码为intset

redis> SADD numbers 1 3 5
(integer) 3
redis> OBJECT ENCODING numbers
"intset"

不过,只要向这个只包含整数元素的集合对象添加一个字符串元素,集合对象的编码转移操作就会被执行

redis> SADD numbers "seven"
integer) 1
redis> OBJECT ENCODING numbers
"hashtable"

除此之外,如果创建一个包含512个整数元素的集合对象,那么对象的编码应该会是intset

redis> EVAL "for i=1, 512 do redis.call('SADD', KEYS[1], i) end" 1 integers
(nil)
redis> SCARD integers
(integer) 512
redis> OBJECT ENCODING integers
"intset"

但是,只要再向集合添加一个新的整数元素,使得这个集合的元素数量变成513,那么对象的编码转换操作就会被执行

redis> SADD integers 10086
(integer) 1
redis> SCARD integers
(integer) 513
redis> OBJECT ENCODING integers
"hashtable"

集合对象的命令(包括不同编码情况下的实现方法)

redis整数集合 redis存集合对象_redis整数集合_03


redis整数集合 redis存集合对象_inset_04


redis整数集合 redis存集合对象_数据类型_05

集合对象相关命令

集合内操作

1.添加元素-sadd

在使用sadd命令向集合中添加元素的时候,sadd命令会自动忽略已存在的元素,只将不存在于集合的新元素添加到集合中

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

如果给定的元素并不存在于集合当中,那么srem命令将忽略不存在的元素,只移除那些确实存在的元素

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.将元素从一个集合移动到另一个集合-smove

smove命令允许用户将指定的元素从源集合移动到目标集合:

smove source target element

smove命令在移动操作成功执行时返回1
如果指定的元素并不存在于源集合,那么smove命令将返回0,表示移动操作执行失败
即使想要移动的元素已经存在于目标集合,smove命令仍然会将指定的元素从源集合移动到目标集合,并覆盖目标集合中的相同元素
从结果来看,这种移动不会改变目标集合包含的元素,只会导致被移动的元素从源集合中消失

4.计算元素个数-scard

scard key

scard的时间复杂度为O(1),它不会遍历集合所有元素,而是直接用Redis内部的变量

127.0.0.1:6379> scard myset
(integer) 1

5.判断元素是否在集合中-sismember

sismember key element

如果给定元素element在集合内返回1,反之返回0,例如

127.0.0.1:6379> sismember myset c
(integer) 1

6.随机从集合内 返回 指定个数 元素-srandmember

当count参数的值大于集合包含的元素数量时,srandmember命令将返回集合包含的所有元素
如果count参数的值为负数,那么srandmember命令将随机返回abs(count)个元素,并且在这些元素当中允许出现重复的元素

srandmember key [count]

[count]是可选参数,如果不写默认为1,例如

127.0.0.1:6379> srandmember myset 2
1) "a"
2) "c"
127.0.0.1:6379> srandmember myset
"d"

7.从集合内随机弹出(删除)元素-spop

spop key

spop操作可以从集合中随机弹出一个元素,例如下面代码是一次spop后,集合元素变为"d b a"

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]参数,默认是1
srandmemberspop都是随机从集合选出元素,两者不同的是spop命令执行后,元素会从集合中删除,而srandmember不会

8.获取所有元素-smembers

因为Redis集合以无序的方式存储元素,并且smembers 命令在获取集合元素时也不会对元素进行任何排序动作,所以根据元素添加顺序的不同,2个包含相同元素的集合在执行smembers 命令时的结果也可能会有所不同

smembers key

下面代码获取集合myset所有元素,并且返回结果是无序的

127.0.0.1:6379> smembers myset
1) "d"
2) "b"
3) "a"

smemberslrangehgetall都属于比较重的命令,如果元素过多存在阻塞Redis的可能性,这时候可以使用sscan来完成

集合间操作

现在有两个集合,它们分别是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

sinter key [key ...]

例如下面代码是求user:1:follow和user:2:follow两个集合的交集,返回结果是sports、it

127.0.0.1:6379> sinter user:1:follow user:2:follow
1) "sports"
2) "it"

2.求多个集合的并集-suinon

suinon key [key ...]

例如下面代码是求user:1:follow和user:2:follow两个集合的并集,返回结果是sports、it、his、news、music、ent

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

sdiff key [key ...]

例如下面代码是求user:1:follow和user:2:follow两个集合的差集,返回结果是music和his

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 ...]

集合间的运算在元素较多的情况下会比较耗时,所以Redis提供了上面三个命令(原命令+store)将集合间交集、并集、差集的结果保存在destination key中
例如下面操作将user:1:follow和user:2:follow两个集合的交集结果保存在user:1_2:inter中,user:1_2:inter本身也是集合类型

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"

时间复杂度

redis整数集合 redis存集合对象_redis_06

集合对象的使用场景(重要!!!)

集合类型比较典型的使用场景是标签(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
...

(3)计算用户共同感兴趣的标签
可以使用sinter命令,来计算用户共同感兴趣的标签,如下代码所示

sinter user:1:tags user:2:tags

总结(重要!!!)

集合类型的应用场景通常为以下几种

  • sadd=Tagging(标签)
  • spop/srandmember=Random item(生成随机数,比如抽奖)
  • sadd+sinter=Social Graph(社交需求)