前面的hash数据结构可以提供很强大的查询能力,但是因为底层采用hashmap,也就是数组加链表的形式,数据量大了以后还是会受限于链表的查询速度。想要在海量数据情况下获得更高的查询速度,就需要用用到一个新的数据类型,set。
我是T型人小付,一位坚持终身学习的互联网从业者。喜欢我的博客欢迎在csdn上关注我,如果有问题欢迎在底下的评论区交流,谢谢。
set数据类型
如果我们将hash数据类型的field内容去掉,而直接将value保存在field的位置,就变成了set。因为之前是存储field的,所以set中的元素不能重复。
常用操作
- sadd key member1 [member2 …]
往set中添加元素,不会按照输入的前后顺序排列
127.0.0.1:6380> sadd set1 james kobe durant embiid davis
(integer) 5
127.0.0.1:6380> smembers set1
1) "embiid"
2) "durant"
3) "kobe"
4) "james"
5) "davis"
127.0.0.1:6380>
- smembers key
查询set中的所有元素,如上所示
- srem key member1 [member2 …]
删除set中的元素,注意删除元素后剩下的元素进行了重新排列
127.0.0.1:6380> smembers set1
1) "embiid"
2) "durant"
3) "kobe"
4) "james"
5) "davis"
127.0.0.1:6380> srem set1 durant kobe
(integer) 2
127.0.0.1:6380> smembers set1
1) "james"
2) "embiid"
3) "davis"
127.0.0.1:6380>
- scard key
获取集合数据个数
127.0.0.1:6380> smembers set1
1) "james"
2) "embiid"
3) "davis"
127.0.0.1:6380> scard set1
(integer) 3
127.0.0.1:6380>
- sismember key member
判断一个元素是不是在set中存在,存在返回1,不存在返回0
127.0.0.1:6380> sismember set1 kobe
(integer) 0
127.0.0.1:6380> sismember set1 james
(integer) 1
127.0.0.1:6380> smembers set1
1) "james"
2) "embiid"
3) "davis"
127.0.0.1:6380>
针对某些随机推荐的场景,例如有10个话题,用户关注了其中的3个。可以把每个话题的当天5个热点新闻放到一个set中,在给用户推送关注的3个set的时候在另外7个set中随机选择一些热点新闻也推送给用户,提高用户留存。
- srandmember key [count]
从set中随机获取一个或多个元素,原set不变
127.0.0.1:6380> smembers set1
1) "durant"
2) "james"
3) "embiid"
4) "davis"
5) "kobe"
127.0.0.1:6380> srandmember set1 2
1) "embiid"
2) "kobe"
127.0.0.1:6380>
- spop key
随机返回一个元素,并从原set删除
127.0.0.1:6380> spop set1
"embiid"
127.0.0.1:6380> smembers set1
1) "durant"
2) "james"
3) "davis"
4) "kobe"
127.0.0.1:6380>
有了这两个操作,redis可以用于随机推荐类信息检索,例如随机点歌,热点新闻推荐等等。
微博上推荐好友或者大v场景,会显示两个人的共同好友;或者推荐一些好友关注但是我没有关注的微博账号;订阅微信公众号的时候会显示有多少好友也关注了;这些场景都涉及到了两个set的比对。
求两个集合的交,并,差集:
- sinter key1 [key2]
- sunion key1 [key2]
- sdiff key1 [key2]
127.0.0.1:6380> sadd set2 a b c d e
(integer) 5
127.0.0.1:6380> sadd set3 b c g k
(integer) 4
127.0.0.1:6380> sinter set2 set3
1) "c"
2) "b"
127.0.0.1:6380> sunion set2 set3
1) "g"
2) "k"
3) "c"
4) "a"
5) "b"
6) "e"
7) "d"
127.0.0.1:6380> sdiff set2 set3
1) "d"
2) "a"
3) "e"
127.0.0.1:6380> sdiff set3 set2
1) "g"
2) "k"
127.0.0.1:6380>
求两个集合的交,并,差集,并储存到某个集合中:
- sinterstore destination key1 [key2]
- sunionstore destination key1 [key2]
- sdiffstore destination key1 [key2]
127.0.0.1:6380> sdiffstore set4 set3 set2
(integer) 2
127.0.0.1:6380> smembers set4
1) "g"
2) "k"
127.0.0.1:6380>
从原始集合移动一个数据到目的集合:
- smove source destination member
127.0.0.1:6380> smembers set2
1) "c"
2) "a"
3) "b"
4) "e"
5) "d"
127.0.0.1:6380> smembers set3
1) "k"
2) "g"
3) "c"
4) "b"
127.0.0.1:6380> smove set2 set3 a
(integer) 1
127.0.0.1:6380> smembers set2
1) "c"
2) "b"
3) "e"
4) "d"
127.0.0.1:6380> smembers set3
1) "k"
2) "g"
3) "c"
4) "b"
5) "a"
127.0.0.1:6380>
redis可以通过set实现同类信息的关联检索,二度关联搜索,深度关联搜索等。
注意事项
- 不允许重复,重复赋值会失败
- 虽说是由hash转变而来,但是不允许用hash的操作对set进行操作
实际案例
- OA系统鉴权问题
公司共有1200名员工,20多种角色,一共有300多项业务和25000多种数据。每种角色可以操作部分业务和数据,每名员工对应几个角色。如何快速进行鉴权?
思路:可以为每个角色创建一个set,里面放所有的权限。将一个用户对应的所有角色拥有的权限取并集就是该用户所有的权限了。
127.0.0.1:6380> sadd role:1 add delete move
(integer) 3
127.0.0.1:6380> sadd role:2 add modify move
(integer) 3
127.0.0.1:6380> sunionstore user:1 role:1 role:2
(integer) 4
127.0.0.1:6380> smembers user:1
1) "move"
2) "modify"
3) "add"
4) "delete"
127.0.0.1:6380>
这时候来了一个操作,如何判断该用户有没有这个操作的权限呢?
两种方法都可以,第一种是取出全部权限由业务代码去判断;第二种是直接在redis中判断
127.0.0.1:6380> smembers user:1
1) "move"
2) "modify"
3) "add"
4) "delete"
127.0.0.1:6380> sismember user:1 modify
(integer) 1
127.0.0.1:6380>
这里我们推荐第一种方法,将数据层和业务层分开。
- 网站访问数据统计
分别获取网站的PV,UV,IP?
PV:刷新一次网页统计一次
UV: 统计访问的用户数,根据cookie来统计
IP: 统计访问的IP数
PV比较简单,直接利用string类型的incr
命令就可以做到。主要是UV和IP,可以利用set中元素不能重复的特点来进行统计。
127.0.0.1:6380> sadd ips 1.1.1.1
(integer) 1
127.0.0.1:6380> sadd ips 1.1.1.2
(integer) 1
127.0.0.1:6380> sadd ips 1.1.1.3
(integer) 1
127.0.0.1:6380> sadd ips 1.1.1.2
(integer) 0
127.0.0.1:6380> sadd ips 1.1.1.1
(integer) 0
127.0.0.1:6380> scard ips
(integer) 3
127.0.0.1:6380>
根据去重特性还可以创建网站的黑白名单。