欢迎光临
我们一直在努力

Redis 解锁:C++ 实战深度探索 Set 数据类型

你的标题是 “Redis 解锁:C++ 实战深度探索 Set 数据类型”,看起来像是想深入探讨 Redis 的 Set 数据类型在 C++ 中的实战用法,同时可能把“解锁”理解为“解锁分布式锁”的意思(因为 Redis 分布式锁和 SET 命令关系非常密切),或者单纯想全面掌握 Set 类型。

下面我把两个方向都给你讲清楚,并重点用 redis-plus-plus(目前最流行的 C++ Redis 客户端)来写实战代码。

1. Redis Set 数据类型核心特性与典型用法

Redis 的 Set 是无序、不重复的字符串集合,底层实现有两种:

  • intset(小集合 + 全是整数时)
  • hashtable(ziplist 编码已废弃,新版用 dict)

常用命令(时间复杂度几乎都是 O(1) 或 O(N) 很小):

命令作用时间复杂度示例
SADD 添加一个或多个元素 O(1) SADD myset “apple” “banana”
SREM 删除元素 O(1) SREM myset “apple”
SISMEMBER 判断元素是否存在 O(1) SISMEMBER myset “apple”
SCARD 获取集合元素个数 O(1) SCARD myset
SMEMBERS 获取所有元素 O(N) SMEMBERS myset
SRANDMEMBER 随机返回一个/多个元素 O(1) / O(N) SRANDMEMBER myset 3
SPOP 随机弹出并删除元素 O(1) SPOP myset
SUNION 并集 O(N) SUNION set1 set2
SINTER 交集 O(N) SINTER set1 set2
SDIFF 差集 O(N) SDIFF set1 set2

典型业务场景:

  • 抽奖系统(随机抽取用户)
  • 标签系统(用户打标签,去重)
  • 共同好友、共同关注
  • 访问用户去重(UV 统计)
  • 黑名单 / 白名单
  • 秒杀商品库存校验(但更常用 string 或 lua)

2. C++ 中使用 redis-plus-plus 操作 Set(推荐方式)

先安装 redis-plus-plus(基于 hiredis,线程安全,支持连接池)

# 假设你用 vcpkg 或自己编译
vcpkg install redis-plus-plus

基本连接与 Set 操作示例

#include <sw/redis++/redis++.h>
#include <iostream>
#include <vector>
#include <string>

using namespace sw::redis;

int main() {
try {
// 单节点连接(推荐使用连接池)
ConnectionOptions opts;
opts.host = "127.0.0.1";
opts.port = 6379;
// opts.password = "yourpass"; // 如有密码

// 连接池(生产推荐)
ConnectionPoolOptions pool_opts;
pool_opts.size = 16; // 连接数
pool_opts.wait_timeout = std::chrono::milliseconds(100);
pool_opts.connection_timeout = std::chrono::seconds(5);

auto redis = Redis(RedisURI("tcp://127.0.0.1:6379"), pool_opts);

// ——————— Set 操作 ———————
std::string key = "myset:fruits";

// 清空测试数据(慎用生产环境)
redis.del(key);

// SADD 添加元素(支持批量)
long long cnt = redis.sadd(key, {"apple", "banana", "orange", "apple"}); // apple 只加一次
std::cout << "Added " << cnt << " unique elements\\n"; // 输出 3

// SISMEMBER 判断存在
if (redis.sismember(key, "banana")) {
std::cout << "banana exists\\n";
}

// SCARD 元素个数
std::cout << "Size: " << *redis.scard(key) << "\\n"; // 3

// SMEMBERS 获取全部(小心大集合)
std::vector<std::string> members;
redis.smembers(key, std::back_inserter(members));
std::cout << "All members: ";
for (const auto& m : members) std::cout << m << " ";
std::cout << "\\n";

// SRANDMEMBER 随机取(不删除)
auto rand_one = redis.srandmember(key);
if (rand_one) std::cout << "Random: " << *rand_one << "\\n";

// SPOP 随机弹出并删除
auto popped = redis.spop(key);
if (popped) std::cout << "Popped: " << *popped << "\\n";

// SREM 删除
redis.srem(key, "orange");

// 集合运算示例
redis.sadd("set:a", {"1", "2", "3", "4"});
redis.sadd("set:b", {"3", "4", "5", "6"});

std::vector<std::string> inter;
redis.sinter({"set:a", "set:b"}, std::back_inserter(inter));
std::cout << "Intersection: ";
for (auto& v : inter) std::cout << v << " "; // 3 4
std::cout << "\\n";

} catch (const Error& e) {
std::cerr << "Redis error: " << e.what() << std::endl;
return 1;
}

return 0;
}

3. Redis 分布式锁才是“解锁”最经典的场景(SET + NX + PX)

Redis 官方推荐的单节点分布式锁就是用 SET 命令(不是 Set 类型,而是 String 类型):

SET lock:order:12345 "uuid-xxx-clientA" NX PX 30000

  • NX:不存在才设置(加锁)
  • PX:毫秒级过期(防死锁)
  • value 必须唯一(通常是 UUID + 客户端标识)

C++ 实现简单版分布式锁(非 Redlock)

#include <sw/redis++/redis++.h>
#include <string>
#include <random>
#include <chrono>
#include <thread>

class SimpleRedisLock {
private:
Redis& _redis;
std::string _lock_key;
std::string _lock_value;
std::chrono::milliseconds _expire_ms;

public:
SimpleRedisLock(Redis& r, const std::string& key, int expire_seconds = 30)
: _redis(r), _lock_key(key),
_expire_ms(std::chrono::seconds(expire_seconds)) {}

bool try_lock() {
// 生成唯一 value(简单示例,可用 uuid 库)
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(10000000, 99999999);
_lock_value = "client-" + std::to_string(dis(gen));

auto reply = _redis.set(_lock_key, _lock_value,
std::chrono::duration_cast<std::chrono::milliseconds>(_expire_ms),
UpdateType::ONLY_IF_NOT_EXIST); // 等价 NX

return reply && *reply; // true 表示加锁成功
}

bool unlock() {
// 安全解锁:用 Lua 脚本保证原子性
static const char* lua_script = R"(
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
)"
;

auto res = _redis.eval<long long>(lua_script, {_lock_key}, {_lock_value});
return res && *res == 1;
}

~SimpleRedisLock() {
unlock(); // 析构自动尝试释放(视业务可去掉)
}
};

// 使用示例
void process_order(Redis& redis) {
SimpleRedisLock lock(redis, "lock:order:12345", 30);

if (!lock.try_lock()) {
std::cout << "获取锁失败\\n";
return;
}

std::cout << "获得锁,开始处理订单…\\n";
std::this_thread::sleep_for(std::chrono::seconds(5)); // 模拟业务

lock.unlock();
std::cout << "订单处理完成,已释放锁\\n";
}

更安全的做法:

  • 用 redis-plus-plus 自带的 Redlock(多节点)
  • 或引入 Redisson 的思想,用更完善的续期机制(watchdog)

// redis-plus-plus 内置 Redlock 用法(需多个 master 节点)
auto redlock = Redlock({node1, node2, node3, node4, node5});
if (redlock.lock("my-resource", std::chrono::seconds(30))) {
// 业务
redlock.unlock();
}

总结对比

需求推荐数据类型核心命令C++ 库支持度
去重、标签、抽奖 Set SADD / SPOP / SRANDMEMBER 非常好
分布式锁(单节点) String SET NX PX 优秀
高可用分布式锁 String Redlock 算法 redis++ 支持
排行榜 Sorted Set ZADD / ZRANGE

如果你真正想问的是 Set 类型 的深度实战,还是 分布式锁的 C++ 实现,可以再补充具体场景,我可以继续给你更细致的代码或优化方案(Lua 续期、Redlock 完整实现、 fencing token 等)。

赞(0)
未经允许不得转载:171主机测评 » Redis 解锁:C++ 实战深度探索 Set 数据类型
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址