题目
Redis Set 的原理和使用场景
答案
@[toc]
---
Set 集合对象是一个【**无序且唯一**】的键值集合,所谓无序,指的是它的【**存储顺序和插入的先后顺序无关**】。
一个集合最多可以存储 `2^32-1` 个元素,除了支持集合内的增删改查,同时还支持多个集合之间的交集、并集、差集等操作。
Set 类型和 List 类型的区别如下:
- List 可以存储重复元素,Set 只能存储非重复元素;
- List 是按照元素的先后顺序存储元素的,而 Set 则是无序方式存储元素的。
这么来看其实和 Java 中 Set / List 也差不多~
## 使用的编码(底层实现)
- `REDIS_ENCODING_INTSET`:整数集合 intset。该编码的使用场景:集合对象保存的所有元素都是整数值,并且集合对象保存的元素数量不超过 512 个

- `REDIS_ENCODING_HT`:字典 dict。该编码的使用场景:不符合 REDIS_ENCODING_INTSET 使用场景的就用这个

字典的每个键都是一个字符串对象,每个字符串对象包含了一个集合元素,而<u>字典的值则全部被设置为 NULL</u>
## 常用命令
- `SADD key member [member ...]`:往集合 key 中存入元素,元素存在则忽略,若 key 不存在则新建
- `SREM key member [member ...]`:从集合 key 中删除元素
- `SMEMBERS key`:获取集合 key 中所有元素
- `SCARD key`:获取集合 key 中的元素个数
- `SISMEMBER key member`:判断 member 元素是否存在于集合 key 中
- `SRANDMEMBER key [count]`:从集合 key 中随机选出 count 个元素,元素不从 key 中删除
- `SPOP key [count]`:从集合 key 中随机选出 count 个元素,元素从 key 中删除
- ......
## 使用场景
### 点赞

假如上面的微博 ID 是 t1001,当前登录用户的 ID 是 u3001
可以用 key = `like:t1001` 来维护 t1001 这条微博的所有点赞用户,value 就是点赞用户的 id:

- 点赞了这条微博:`sadd like:t1001 u3001`(Set 对象的去重性天然地保证了同一个用户无法对同一个微博点 2 次赞)
- 取消点赞:`srem like:t1001 u3001`
- 是否点赞:`sismember like:t1001 u3001`
- 点赞这条微博的所有用户:`smembers like:t1001`
- 这条微博的点赞数量:`scard like:t1001`
### 共同关注
Set 类型支持集合运算,所以可以用来计算共同关注的好友、我关注的人也关注了他、可能认识的人等......
- 用 key = `user_id:follow` 表示 user_id 这个用户关注的人,对应的 value 就是 user_id 这个用户关注的人的 user_id
- 用 key = `user_id:fans` 表示 user_id 这个用户的粉丝(关注 user_id 这个用户的人),对应的 value 就是粉丝的 user_id
比如:user_id = 1001 的用户和 user_id = 1002 的用户相互关注:
- `sadd 1001:follow 1002`
- `sadd 1002:fans 1001`
- `sadd 1002:follow 1001`
- `sadd 1001:fans 1002`
1001和 1002 共同关注的好友:
- `sinter 1001:follow 1002:follow`
我(user_id = 1001)关注的人也关注了他(user_id = 1002)(取交集):
- `sinter 1001:follow 1002:fans`
给我(user_id = 1001)推荐 user_id = 1002 关注的人(差集),也就是可能认识的人功能了 :
- `sdiff 1002:follow 1001:follow`
### 抽奖
存储某活动中中奖的用户名 ,Set 类型因为有去重功能,可以保证同一个用户不会中奖两次。
key 为抽奖活动名(比如 lucky),value 为员工名称,把所有员工名称放入抽奖箱 :
- `SADD lucky Tom Jerry John Sean Marry Lindy Sary Mark`
如果允许重复中奖,可以使用 `SRANDMEMBER` 命令
- `SRANDMEMBER lucky 1`:抽 1 个奖
- `SRANDMEMBER lucky 3`:抽 3 个奖
如果不允许重复中奖,可以使用 `SPOP` 命令
- `SPOP lucky 1`:抽 1 个奖
- `SPOP lucky 3`:抽 3 个奖
