Set 集合
SADD/SINTER、标签与去重。
Set 集合:题目标签、共同推荐与抽奖去重
这一课你将能做到
- 熟练对集合做增删、判重、取数量和随机抽取
- 用交集、并集、差集实现「大家都在做的题」和个性化推荐
- 用集合完成抽奖去重,并说清楚 Set 底层为什么又快又省
为什么要学
Set 是无序、元素唯一的字符串集合。你在数学里学过的集合运算——交集、并集、差集——Redis 直接给你做成了原子命令。
本站有几个需求几乎是为 Set 量身定做的:
- 每道题有若干标签(数组、哈希表、动态规划),每个用户也有「已做过的题」集合;
- 推荐页要展示「和你水平相近的人都做了、但你还没做」的题——这是一个差集运算;
- 节日活动抽奖:一个用户只能中奖一次,奖品随机抽出,且要当场知道还剩多少人没抽。
判重 + 集合运算 + 随机抽取,这三件事用关系型数据库做要写一堆 JOIN 和 DISTINCT,用 Set 各是一条命令。
先建立图像
标签集合:
tags:problem:501 = { "array", "hash", "easy" }
tags:problem:502 = { "array", "dp", "hard" }
交集 SINTER → { "array" } 两道题的共同标签
用户做题集合(元素是题目 ID):
solved:user:1001 = { "501", "502", "503" }
solved:user:1002 = { "502", "503", "504" }
差集 SDIFF(1002 做了而 1001 没做)→ { "504" } ← 推荐给 1001
底层方面,Redis 7 的 Set 在元素少且短时用整数集合(intset)或列表编码,元素变多自动转为哈希表,判重和增删都是 O(1),这个转换对使用者透明。
跟着做
步骤 1:增删与查看
127.0.0.1:6379> SADD tags:problem:501 array hash easy
(integer) 3
127.0.0.1:6379> SADD tags:problem:501 array
(integer) 0
第二次添加返回 0——元素已存在,集合自动去重,这是 Set 的核心价值。
127.0.0.1:6379> SMEMBERS tags:problem:501
1) "easy"
2) "hash"
3) "array"
127.0.0.1:6379> SCARD tags:problem:501
(integer) 3
127.0.0.1:6379> SISMEMBER tags:problem:501 dp
(integer) 0
127.0.0.1:6379> SREM tags:problem:501 easy
(integer) 1
注意 SMEMBERS 返回顺序不保证,且集合很大时会一次拉全量,大集合请改用 SSCAN 游标遍历:
127.0.0.1:6379> SSCAN solved:user:1001 0 COUNT 100
1) "0"
2) 1) "501"
2) "502"
步骤 2:用两个集合记录做题情况
127.0.0.1:6379> SADD solved:user:1001 501 502 503
(integer) 3
127.0.0.1:6379> SADD solved:user:1002 502 503 504
(integer) 3
127.0.0.1:6379> SISMEMBER solved:user:1001 504
(integer) 0
前端展示做题进度时,SCARD 给出已做总数,SISMEMBER 决定某道题要不要打勾,全是常数时间。
步骤 3:交集——共同标签与「都做过」
127.0.0.1:6379> SADD tags:problem:502 array dp hard
(integer) 3
127.0.0.1:6379> SINTER tags:problem:501 tags:problem:502
1) "array"
判断两个用户有没有共同做过的题,有交集就推荐「切磋」: