Redis 数据结构精讲:Set 集合的原理、命令与实战场景
大家好,今天我们来聊聊 Redis 中一个非常实用的数据结构 ——Set(集合)。它和我们熟悉的列表(List)有很多相似之处,比如都能存储多个字符串类型的元素,但也有几个关键区别:Set 中的元素是无序的,而且不允许重复。更重要的是,Redis 的 Set 天生支持交集、并集、差集这些集合操作,在很多业务场景下都能发挥奇效。
一、Set 集合的基本概念
先看个直观的例子,比如我们要记录用户user:1的兴趣爱好:
可以看到,Set 的 Key 是user:1:subscribe,对应的 Value 是一个包含Basketball、IT、Music等多个元素的集合。这些元素没有固定顺序,也不会重复,完美契合了 “用户兴趣标签” 这类场景的需求。
二、Set 集合的基础命令详解
我们先从最常用的基础命令开始,逐个拆解它们的用法、时间复杂度和实战示例。
1. SADD:添加元素到集合
作用:将一个或多个元素添加到集合中,重复元素会被自动忽略。
语法:
SADD key member [member \.\.\.\]时间复杂度:O(1)
返回值:本次成功添加的元素个数
redis> SADD myset "Hello" (integer) 1 redis> SADD myset "World" (integer) 1 redis> SADD myset "World" (integer) 0 # 重复元素添加失败,返回0 redis> SMEMBERS myset 1) "Hello" 2) "World"2. SMEMBERS:获取集合中所有元素
作用:返回集合中的所有元素,注意元素的顺序是无序的。
语法:
SMEMBERS key时间复杂度:O (N)(N 为集合元素个数)
返回值:集合中所有元素的列表
redis> SADD myset "Hello" (integer) 1 redis> SADD myset "World" (integer) 1 redis> SMEMBERS myset 1) "Hello" 2) "World"3. SISMEMBER:判断元素是否存在于集合中
作用:判断一个元素是否是集合的成员。
语法:
SISMEMBER key member时间复杂度:O(1)
返回值:1 表示元素存在,0 表示元素不存在或集合本身不存在
redis> SADD myset "one" (integer) 1 redis> SISMEMBER myset "one" (integer) 1 redis> SISMEMBER myset "two" (integer) 04. SCARD:获取集合的元素个数
作用:获取集合的基数(cardinality),也就是集合中元素的数量。
语法:
SCARD key时间复杂度:O(1)
返回值:集合中的元素个数
redis> SADD myset "Hello" (integer) 1 redis> SADD myset "World" (integer) 1 redis> SCARD myset (integer) 25. SPOP:随机删除并返回元素
作用:从集合中随机删除并返回一个或多个元素(因为集合是无序的,删除的元素是随机的)。
语法:
SPOP key [count\]时间复杂度:O (N)(N 为 count 的值)
返回值:被删除的元素
redis> SADD myset "one" (integer) 1 redis> SADD myset "two" (integer) 1 redis> SADD myset "three" (integer) 1 redis> SPOP myset "one" redis> SMEMBERS myset 1) "two" 2) "three" redis> SADD myset "four" (integer) 1 redis> SADD myset "five" (integer) 1 redis> SPOP myset 3 1) "four" 2) "five" 3) "two"6. SMOVE:移动元素到另一个集合
作用:将一个元素从源集合移动到目标集合中。
语法:
SMOVE source destination member时间复杂度:O(1)
返回值:1 表示移动成功,0 表示元素不存在或移动失败
redis> SADD myset "one" (integer) 1 redis> SADD myset "two" (integer) 1 redis> SADD myotherset "three" (integer) 1 redis> SMOVE myset myotherset "two" (integer) 1 redis> SMEMBERS myset 1) "one" redis> SMEMBERS myotherset 1) "three" 2) "two"7. SREM:删除集合中的指定元素
作用:从集合中删除一个或多个指定的元素。
语法:
SREM key member [member \.\.\.\]时间复杂度:O (N)(N 为要删除的元素个数)
返回值:本次操作成功删除的元素个数
redis> SADD myset "one" (integer) 1 redis> SADD myset "two" (integer) 1 redis> SADD myset "three" (integer) 1 redis> SREM myset "one" (integer) 1 redis> SMEMBERS myset 1) "two" 2) "three"三、集合间的高级操作:交集、并集、差集
这是 Redis Set 最强大的功能之一,支持多个集合之间的数学运算,比如交集(inter)、并集(union)、差集(diff)。
1. SINTER:获取多个集合的交集
作用:返回所有给定集合的交集元素。
语法:
SINTER key [key \.\.\.\]时间复杂度:O (N*M)(N 是最小集合的元素个数,M 是集合的数量)
返回值:交集的元素列表
redis> SADD key1 "a" (integer) 1 redis> SADD key1 "b" (integer) 1 redis> SADD key1 "c" (integer) 1 redis> SADD key2 "c" (integer) 1 redis> SADD key2 "d" (integer) 1 redis> SADD key2 "e" (integer) 1 redis> SINTER key1 key2 1) "c"2. SINTERSTORE:将交集结果保存到目标集合
作用:和SINTER功能类似,但会把交集结果存储到指定的目标集合中,而不是直接返回。
语法:
SINTERSTORE destination key [key \.\.\.\]时间复杂度:O(N*M)
返回值:交集中元素的个数
redis> SADD key1 "a" (integer) 1 redis> SADD key1 "b" (integer) 1 redis> SADD key1 "c" (integer) 1 redis> SADD key2 "c" (integer) 1 redis> SADD key2 "d" (integer) 1 redis> SADD key2 "e" (integer) 1 redis> SINTERSTORE key key1 key2 (integer) 1 redis> SMEMBERS key 1) "c"3. SUNION:获取多个集合的并集
作用:返回所有给定集合的并集元素(合并所有元素并去重)。
语法:
SUNION key [key \.\.\.\]时间复杂度:O (N)(N 是所有集合元素的总数)
返回值:并集的元素列表
redis> SADD key1 "a" (integer) 1 redis> SADD key1 "b" (integer) 1 redis> SADD key1 "c" (integer) 1 redis> SADD key2 "c" (integer) 1 redis> SADD key2 "d" (integer) 1 redis> SADD key2 "e" (integer) 1 redis> SUNION key1 key2 1) "a" 2) "b" 3) "c" 4) "d" 5) "e"4. SUNIONSTORE:将并集结果保存到目标集合
作用:和SUNION功能类似,但会把并集结果存储到指定的目标集合中。
语法:
SUNIONSTORE destination key [key \.\.\.\]时间复杂度:O(N)
返回值:并集中元素的个数
redis> SADD key1 "a" (integer) 1 redis> SADD key1 "b" (integer) 1 redis> SADD key1 "c" (integer) 1 redis> SADD key2 "c" (integer) 1 redis> SADD key2 "d" (integer) 1 redis> SADD key2 "e" (integer) 1 redis> SUNIONSTORE key key1 key2 (integer) 5 redis> SMEMBERS key 1) "a" 2) "b" 3) "c" 4) "d" 5) "e"5. SDIFF:获取多个集合的差集
作用:返回第一个集合与其他集合的差集元素(只保留第一个集合中独有的元素)。
语法:
SDIFF key [key \.\.\.\]时间复杂度:O(N)
返回值:差集的元素列表
redis> SADD key1 "a" (integer) 1 redis> SADD key1 "b" (integer) 1 redis> SADD key1 "c" (integer) 1 redis> SADD key2 "c" (integer) 1 redis> SADD key2 "d" (integer) 1 redis> SADD key2 "e" (integer) 1 redis> SDIFF key1 key2 1) "a" 2) "b"6. SDIFFSTORE:将差集结果保存到目标集合
作用:和SDIFF功能类似,但会把差集结果存储到指定的目标集合中。
语法:
SDIFFSTORE destination key [key \.\.\.\]时间复杂度:O(N)
返回值:差集中元素的个数
redis> SADD key1 "a" (integer) 1 redis> SADD key1 "b" (integer) 1 redis> SADD key1 "c" (integer) 1 redis> SADD key2 "c" (integer) 1 redis> SADD key2 "d" (integer) 1 redis> SADD key2 "e" (integer) 1 redis> SDIFFSTORE key key1 key2 (integer) 2 redis> SMEMBERS key 1) "a" 2) "b"四、Set 命令速查表
这里整理了所有常用的 Set 命令,方便大家快速查阅:
| 命令 | 功能描述 | 时间复杂度 |
|---|---|---|
SADD key member [\.\.\.\] | 添加元素到集合 | O(1) |
SMEMBERS key | 获取集合所有元素 | O(N) |
SISMEMBER key member | 判断元素是否存在 | O(1) |
SCARD key | 获取集合元素个数 | O(1) |
SPOP key [count\] | 随机删除并返回元素 | O (N)(N 为 count) |
SMOVE src dest member | 移动元素到另一个集合 | O(1) |
SREM key member [\.\.\.\] | 删除指定元素 | O (N)(N 为删除的元素数) |
SINTER key1 key2 [\.\.\.\] | 获取多个集合的交集 | O(N*M) |
SINTERSTORE dest key1 [\.\.\.\] | 存储交集结果到目标集合 | O(N*M) |
SUNION key1 key2 [\.\.\.\] | 获取多个集合的并集 | O(N) |
SUNIONSTORE dest key1 [\.\.\.\] | 存储并集结果到目标集合 | O(N) |
SDIFF key1 key2 [\.\.\.\] | 获取多个集合的差集 | O(N) |
SDIFFSTORE dest key1 [\.\.\.\] | 存储差集结果到目标集合 | O(N) |
五、Set 集合的底层编码实现
Redis 的 Set 类型有两种底层编码方式,会根据场景自动切换:
intset(整数集合):当集合中的元素都是整数,且元素个数不超过 512 个时,Redis 会使用
intset编码存储,这种方式非常节省内存。127.0.0.1:6379> SADD setkey 1 2 3 4 (integer) 4 127.0.0.1:6379> OBJECT encoding setkey "intset"hashtable(哈希表):当集合中的元素包含非整数,或者元素个数超过 512 个时,Redis 会自动切换为
hashtable编码,以保证读写性能。127.0.0.1:6379> SADD setkey 1 2 3 4 ... (添加超过512个元素) 127.0.0.1:6379> OBJECT encoding setkey "hashtable"
六、Set 集合的实战场景
Set 集合的特性,让它在很多业务场景下都能大显身手,这里举几个常见的例子:
1. 给用户添加兴趣标签
我们可以用 Set 存储用户的兴趣标签,天然去重,添加 / 删除都很方便:
SADD user:1:tags tag1 tag2 tag3 SADD user:1:tags tag1 tag4 # 重复的tag1会被忽略2. 给标签添加用户
反过来,也可以用 Set 存储每个标签下的用户列表:
SADD tag:1:users user:1 user:2 user:3 SADD tag:2:users user:1 user:4 user:93. 计算用户的共同好友 / 共同兴趣
利用 Set 的交集功能,我们可以轻松计算两个用户的共同兴趣标签:
# 计算user:1和user:2的共同兴趣 SINTER user:1:tags user:2:tags这种场景在社交平台、推荐系统中非常常见,比如:
计算两个用户的共同好友
找出同时关注了多个话题的用户
统计用户的共同兴趣,实现个性化推荐
总结
Redis 的 Set 集合虽然结构简单,但功能非常强大:无序、去重、支持丰富的集合运算,在标签系统、好友关系、数据去重等场景中都有不可替代的优势。掌握好这些命令和底层原理,就能在开发中灵活运用,写出更高效、更优雅的代码。