Redis Set 基础与 C++ 实战
Redis Set 是一种无序且元素唯一的字符串集合数据结构。本文基于 C++ redis-plus-plus 库演示了 Set 的核心操作与高级应用。
Redis Set 是一种无序且元素唯一的字符串集合数据结构。本文基于 C++ redis-plus-plus 库演示了 Set 的核心操作与高级应用。内容涵盖基础命令如添加成员、获取数量及成员检查,重点解析交集、并集、差集等集合运算在服务器端的高效执行。通过实际代码示例展示了 SISMEMBER、SCARD、SPOP 等命令的用法及时间复杂度优势,帮助开发者利用 Redis 实现去重、权限控制、社交关系分析及任务队列等功能,提升系统性能与开发效率。

Redis Set 是一种无序且元素唯一的字符串集合数据结构。本文基于 C++ redis-plus-plus 库演示了 Set 的核心操作与高级应用。
想象一个魔力袋,你可以往里面扔东西,但有两条特殊规则:
这个'魔力袋'正是 Redis Set 的精准比喻:一个无序的、元素唯一的字符串集合。这使得它能够以惊人的速度进行成员资格检查、数量统计以及复杂的服务器端运算。
使用 SADD 和 SMEMBERS 命令。
SADD 用于添加一个或多个元素。如果元素已存在,Redis 会忽略它。返回值是新成功添加的元素数量。
返回指定 Set 中的所有成员。注意:在拥有数百万元素的超大 Set 上使用此命令可能会暂时阻塞您的 Redis 服务器。后续章节将讨论更安全的替代方案 SSCAN。
#include <iostream>
#include <set>
#include <string>
#include <vector>
#include <iterator>
#include <sw/redis++/redis.h>
// 辅助函数,用于打印容器内容
template<typename T>
void PrintContainer(const T& container) {
for (const auto& elem : container) {
std::cout << elem << " ";
}
std::cout << std::endl;
}
void test1(sw::redis::Redis& redis) {
std::cout << "sadd 和 smembers" << std::endl;
// 清空数据库,确保一个干净的测试环境
redis.flushall();
// 1. 一次添加一个元素
redis.sadd("key", "111");
// 2. 使用初始化列表,一次添加多个元素
redis.sadd("key", {"222", "333", "444"});
// 3. 使用迭代器,从另一个容器中添加多个元素
std::set<std::string> elems = {"555", "666", "777"};
// 返回值是成功插入了多少个元素
redis.sadd("key", elems.begin(), elems.end());
// --- 现在,让我们获取所有元素 ---
std::set<std::string> result;
// 为我们的 C++ set 构建一个插入迭代器
auto it = std::inserter(result, result.end());
// 从 Redis set 中获取所有成员,并插入到我们的 C++ set 中
redis.smembers("key", it);
PrintContainer(result);
}
redis.flushall():清空整个 Redis 数据库,确保测试环境纯净。sadd:redis.sadd("key", "111"); 将字符串 '111' 添加到名为 key 的 Set 中。由于 Set 原本是空的,此命令返回 1。sadd:redis.sadd("key", {"222", "333", "444"}); 展示了 redis-plus-plus 库的一个便捷特性,允许您一次性添加多个元素。这比发送三个独立的命令效率更高。此调用将返回 3。sadd:先填充了一个 C++ 的 std::set,然后使用它的迭代器(elems.begin(), elems.end())将其所有元素添加到 Redis 的 Set 中。这对于将现有 C++ 容器中的数据同步到 Redis 非常有用。smembers 获取数据:
std::set<string> result; 来存放从 Redis 返回的数据。在客户端使用 std::set 是一个绝佳选择,因为它不仅镜像了 Redis Set 的唯一性,还能自动对元素进行排序,便于展示。auto it = std::inserter(result, result.end()); 是关键。我们需要一种方式告诉 redis-plus-plus 应该把接收到的元素放在哪里。inserter 是一种特殊的迭代器,当你给它赋值时,它会调用其关联容器的 insert() 方法。redis.smembers("key", it); 执行命令。redis-plus-plus 获取 key 中的所有成员,并使用我们的迭代器 it 将它们逐一插入到 result 集合中。std::back_inserter 创建一个调用 push_back() 的迭代器。适用于 std::vector, std::list, std::deque 等容器。std::set没有push_back() 方法,因为它需要维护内部的排序。因此,对于 std::set,我们必须使用 std::inserter,它会调用 insert() 方法。PrintContainer 函数将打印 result 集合的内容。由于 std::set 会对其元素进行排序,输出将是按字母/数字顺序排列的。
sadd 和 smembers 111 222 333 444 555 666 777
SISMEMBER 检查一个特定元素是否是 Set 的成员。如果存在,返回 1 (true);如果不存在,返回 0 (false)。性能是 O(1),不依赖于 Set 的大小。
void test2(sw::redis::Redis& redis) {
std::cout << "sismember" << std::endl;
redis.flushall();
redis.sadd("key", {"111", "222", "333", "444"});
// 检查 "111" 是否是集合的成员
bool result = redis.sismember("key", "111");
std::cout << "result:" << result << std::endl;
}
redis-plus-plus 库非常方便地将 Redis 返回的 1 或 0 直接映射为了 C++ 的 bool 类型。因为 '111' 确实在 Set 中,result 将为 true。sismember result:1SCARD 代表 'Set Cardinality'(集合基数),返回一个 Set 中元素的数量。这也是一个 O(1) 操作。
void test3(sw::redis::Redis& redis) {
std::cout << "scard" << std::endl;
redis.flushall();
// 向集合中添加 4 个唯一元素
redis.sadd("key", {"111", "222", "333", "444"});
// 获取集合中的元素个数
long long result = redis.scard("key");
// 返回 4
std::cout << "result:" << result << std::endl;
}
scard result:4SPOP 会从 Set 中随机选择一个元素,将其移除,然后返回给你。这是一种'破坏性读取'。
void test4(sw::redis::Redis& redis) {
std::cout << "spop" << std::endl;
redis.flushall();
redis.sadd("key", {"111", "222", "333", "444"});
// 随机弹出一个元素,spop 的返回值是 Optional<string>
auto result = redis.spop("key");
if (result) {
// 因为返回值是 Optional,我们通过 .value() 来获取原始的 string 内容
std::cout << "result:" << result.value() << std::endl;
} else {
std::cout << "result is empty" << std::endl;
}
}
redis-plus-plus 将返回值包装在 sw::redis::Optional<std::string> 中。这是因为如果你对一个空 Set 执行 spop,Redis 会返回 nil(空)。Optional 类型可以优雅地处理这种情况。111, 222, 333 或 444 之一。Redis 能够在服务器端以极高的效率执行集合的交集 (intersection)、并集 (union) 和差集 (difference) 运算。
SINTER: 计算交集并直接返回给客户端。SINTERSTORE: 计算交集,但不返回,而是将结果存储在一个新的目标 Set 中。void test5(sw::redis::Redis& redis) {
std::cout << "sinter" << std::endl;
redis.flushall();
redis.sadd("key1", {"111", "222", "333", "444"});
redis.sadd("key2", {"111", "222", "444"});
std::set<std::string> result;
auto it = std::inserter(result, result.end());
// 求交集涉及多个 key,我们使用初始化列表来描述
// 将 "key1" 和 "key2" 的交集插入到 result 中
redis.sinter({"key1", "key2"}, it);
PrintContainer(result);
}
sinter 111 222 444void test6(sw::redis::Redis& redis) {
std::cout << "sinterstore" << std::endl;
redis.flushall();
redis.sadd("key1", {"111", "222", "333"});
redis.sadd("key2", {"111", "222", "444"});
// 指定一个 destination ("key3"),将交集结果存储到其中
long long len = redis.sinterstore("key3", {"key1", "key2"});
std::cout << "len:" << len << std::endl;
// 检查 "key3" 中的元素以验证结果
std::set<std::string> result;
auto it = std::inserter(result, result.end());
redis.smembers("key3", it);
PrintContainer(result);
}
sinterstore len:2 111 222返回所有给定集合的全部不重复的元素。适用于好友圈合并、权限合并等场景。
返回那些只存在于第一个集合中,但不在任何后续集合中的元素。适用于好友推荐、内容去重等场景。
SMEMBERS 对于大集合是危险的。SSCAN 提供了安全的替代方案。它使用一个游标 (cursor) 来分批次地返回集合中的元素,每次只返回一小部分,绝不会阻塞服务器。
Redis Set 是一种看似简单却异常强大的数据结构。核心优势包括:
从简单的在线用户统计,到复杂的社交网络好友关系分析,再到智能推荐系统,Redis Set 都能以其优雅和高效提供坚实的解决方案。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online