Redis存储⑧Redis的C++客户端安装和使用

目录

1. 安装 redis-plus-plus

1.1 Ubuntu安装

1.2 Centos安装

2. 编写 HelloWorld

3. C++使用Redis通用命令

3.1 get和set

3.2 exists和del

3.3 keys和expire和ttl和type

4.. C++使用Redis的数据类型

string.cc

hash.cc

list.cc

set.cc

zset.cc

整体的Makefile和util.hpp

本篇完。


1. 安装 redis-plus-plus

C++ 操作 Redis 的库有很多,这里选择使用 redis-plus-plus,这个库的功能强大,使用简单。

Github 地址:GitHub - sewenew/redis-plus-plus: Redis client written in C++

访问不了Github 地址的可以使用Steam++来加速访问。

        redis-plus-plus 是基于 hiredis 实现的。hiredis 是一个 C 语言实现的 redis 客户端,因此需要先安装 hiredis,直接使用包管理器安装即可。

redis-plus-plus本体,只能编译安装,使用编译安装ubuntu会比centos方便很多

1.1 Ubuntu安装

apt install libhiredis-dev

Ubuntu

安装cmake

apt install cmake

具体步骤:

git clone https://github.com/sewenew/redis-plus-plus.git
cd redis-plus-plus

mkdir build

cd build

cmake .. //生成makefile,此处..指向CMakeLists.txt文件所在的目录

make

make install //把刚才的库拷贝到系统目录


1.2 Centos安装

Centos 

yum install hiredis-devel.x86_64 

Centos自带的cmake版本较低,需要先安装cmake3

yum install cmake3

然后使用cmake3构建项目

cd redis-plus-plus

mkdir build  //创建一个build目录是为了让编译生成的临时文件都放到这里,避免污染源代码目录

cd build

cmake3 ..

make

make install

        构建成功后,会在/usr/local/include/中多出sw目录,并且内部包含redis-plus-plus一系列头文件,会在/usr/local/lib/中多出一系列libredis库文件


2. 编写 HelloWorld

        Redis 库最多可以支持到 C++17 版本。(如果是用 Centos,需要注意 gcc/g++ 的版本,看是否支持 C++17。不支持的话,选用 C++11 版本也是可以的)

hello:hello.cc
	g++ -std=c++17 -o $@ $^ /usr/local/lib64/libredis++.a /usr/local/lib/libhiredis.a -pthread
 
.PHONY:clean
clean:
	rm hello

        编译程序时,需要引入的库文件(由于不同系统中安装好的库所在位置会存在差异,因此要注意⽂件路径是否存在。可以用 find 命令来查找库所在的目录):

  1. Redis++ 自己的静态库
  2. hiredis 静态库
  3. 线程库

也可以选择使用动态库链接:

hello.cc:

#include <iostream>
#include <vector>
#include <string>
#include <unordered_map>
#include <sw/redis++/redis++.h>
 
using std::cout;
using std::endl;
using std::vector;
using std::string;
using std::unordered_map;
 
int main()
{
    // 创建Redis对象时,需要在构造函数中指定redis服务器的地址和端口 
    sw::redis::Redis redis("tcp://127.0.0.1:6379");
    // 调用ping方法,让客户端给服务器发了一个PING,然后服务器就会返回一个PONG,就通过返回值获取到 
    string result = redis.ping(); // 测试用,可不写
    std::cout << result << std::endl;

    redis.set("key", "HelloWorld");

    // 使用 get 获取到 key 对应的 value
    auto value1 = redis.get("key");
    // optional 可以隐式转成 bool 类型, 可以直接在 if 中判定. 如果是无效元素, 就是返回 false
    if (value1)
    {
        std::cout << value1.value() << std::endl;
    }
    return 0;
}


3. C++使用Redis通用命令

3.1 get和set

此处的 Optional 可以表示:“非法值” / “无效值”(nil)

        如果直接使用 std::string 来表示,则不方便来表现 nil(无效值);如果使用 std::string* 来标识,虽然可以用 nullptr 表示无效,但是返回指针又会涉及到内存问题。

        boost 很早就引入了 optional 类型,在 C++14 版本中就正式是纳入标准库了。因为用户可能选用的是 C++11 版本,所以作者自己封装了一个 OptionalString。

使用 get 获取到 key 对应的 value:

        也可以不选择 if 来做判断,而是选用 try-catch。但在实际上,C++ 中不太会经常使用 try-catch。原因如下:

  • try-catch 会存在额外的运行时开销,对于性能追求极致的 C++ 来说不太合适。
  • C++的try-catch 相比于其他语言(Java、Python)来说有点弱。

        上面的 value1 ~ value4 都是 optional 类型,sw::redis::OptionalString 不支持 << 运算符的重载。这里也没有必要给 optional 搞一个 << 的重载,只需要把 optional 里面包含的元素取出来即可(optional 可以当作只包含一个元素的容器)。

set:

这里用到了 StringView 这样的类型(是在 sw::redis 命名空间里的).

  • std::string 是可修改的,既能读,也能写。
  • StringView 是只读的(不能修改),针对只读操作,做很多的优化工作,效率比 std::string 更高。(在 C++17 标准库中也提供了一个 std::string_view)

StringView 中的各种操作和 string 类似,只不过是包含了一些只读方法。


运行程序Makefile

输出:


3.2 exists和del

exists

del

进行单元测试时,清除数据操作是放在开始,还是结束呢?

        放在开始更好一些。因为如果放在结束的位置,一旦执行的程序中间出现异常,就可能导致清理代码没有执行到。另一方面,放在开始的位置,当执行完一个操作之后,此时 Redis 里仍然是有数据的,也方便我们手工来检查数据的内容。


3.3 keys和expire和ttl和type

通常,一个迭代器主要是表示一个 “位置”。 STL 中的 5 种迭代器类型:

  1. 输入迭代器('*',"++" 对它来说不会产生影响,主要操作是 '='(把另一个迭代器赋值给这个插入迭代器),例如:it:插入迭代器;it2:普通迭代器。此时 it=it2 相当于是获取到 it2 指向的元素,然后把元素按照 it 当前插入迭代器的位置和动作来执行插入操作,比如 it 是一个 back_insert_iterator,就是把 it2 指向的元素插入到 it 指向的容器末尾,相当于调用了一次 push_back)
  2. 输出迭代器(上面的插入迭代器(“位置” + “动作”)本质上也是一种输出迭代器,标准库中主要有三个:font_insert_iterator(区间开头,往前面插入)、back_insert_iterator(区间末尾,往后面插入)、insert_iterator(区间任意位置,往该位置之前插入),一般不会直接构造出这几个对象(构造函数写起来比较麻烦),一般会使用一些辅助函数来进行构造)
  3. 前向迭代器
  4. 双向迭代器
  5. 随机访问迭代器(最大的优势:以 O(1) 快速定位到指定位置,主要也是由于内存支持随机访问)

        这里直接使用容器作为参数,keys 内部直接操作容器进行插入不就行了吗,为什么还要通过迭代器绕一个大圈子呢?

解耦合。代码要追求高内聚、低耦合。

        keys 这个操作看不到容器,容器也看不到 keys 操作。此时,它们双方都使用迭代器这样一个中间媒介来去进行交互。此时,keys 的迭代器可以指向 vector,也可以指向其它的容器。换句话说,给你一个 vector 容器,它可以搭配 keys 使用,也可以搭配其他函数使用。也就是说,任何一个容器可以和任意函数搭配使用。

expire和ttl:

  • Linux:sleep 是以 s 做单位的
  • Windows:Sleep 是以 ms 做单位的

        系统函数是和系统相关的,同样的功能在不同的系统上可能是完全不同的函数。更好的选择是使用标准库的函数,C++ 提供了 thread -> sleep_for。

type:


4.. C++使用Redis的数据类型

string.cc

注意事项:

  • incr 和 decr 得到的都是 long long 类型。
  • get 得到的是 OptionalString 类型(需要手动转换成数字)
#include <iostream>
#include <vector>
#include <string>
#include <unordered_map>
#include <chrono>
#include <thread>
#include <sw/redis++/redis++.h>  // Redis++客户端库

#include "util.hpp"              // 自定义工具函数

using std::cout;
using std::endl;
using std::vector;
using std::string;
using std::unordered_map;

using sw::redis::Redis;          // Redis客户端对象

using namespace std::chrono_literals;  // 时间字面量支持

/* 测试基本SET/GET操作 */
void test1(Redis& redis) {
    std::cout << "\n=== SET/GET基础测试 ===" << std::endl;
    redis.flushall();  // 清空数据库(仅限测试环境)

    // 设置键值对
    redis.set("key", "111");
    
    // 获取值(返回optional<string>需判空)
    auto value = redis.get("key");
    if (value) {
        std::cout << "初始值: " << *value << endl;
    }

    // 更新值
    redis.set("key", "222");
    value = redis.get("key");
    if (value) {
        std::cout << "更新值: " << *value << endl;
    }
}

/* 测试带过期时间的SET操作 */
void test2(Redis& redis) {
    std::cout << "\n=== 带过期时间的SET操作 ===" << std::endl;
    redis.flushall();

    // 设置10秒过期的键
    redis.set("key", "111", 10s);  // 使用chrono时间单位

    // 休眠3秒后检查剩余时间
    std::this_thread::sleep_for(3s);
    long long ttl = redis.ttl("key");
    std::cout << "剩余存活时间: " << ttl << "秒" << endl;
}

/* 测试条件更新(NX/XX)*/
void test3(Redis& redis) {
    std::cout << "\n=== 条件更新测试 ===" << std::endl;
    redis.flushall();

    redis.set("key", "111");
    
    // 仅当键存在时更新(XX选项)
    // 参数说明:0s表示不修改过期时间,UpdateType::EXIST表示仅更新已存在的键
    redis.set("key", "222", 0s, sw::redis::UpdateType::EXIST);

    auto value = redis.get("key");
    if (value) {
        std::cout << "条件更新结果: " << *value << endl;
    } else {
        std::cout << "更新失败(键不存在)" << endl;
    }
}

/* 测试批量设置MSET */
void test4(Redis& redis) {
    std::cout << "\n=== 批量设置测试 ===" << std::endl;
    redis.flushall();

    // 方法1:使用初始化列表
    // redis.mset({ {"key1","111"}, {"key2","222"}, {"key3","333"} });

    // 方法2:使用容器迭代器
    vector<std::pair<string, string>> kv_pairs = {
        {"key1", "111"},
        {"key2", "222"},
        {"key3", "333"}
    };
    redis.mset(kv_pairs.begin(), kv_pairs.end());

    // 验证设置结果
    auto print_value = [&](const string& key) {
        if (auto val = redis.get(key)) {
            cout << key << " = " << *val << endl;
        }
    };
    print_value("key1");
    print_value("key2");
    print_value("key3");
}

/* 测试批量获取MGET */
void test5(Redis& redis) {
    std::cout << "\n=== 批量获取测试 ===" << std::endl;
    redis.flushall();

    // 初始化测试数据
    vector<std::pair<string, string>> data = {
        {"key1", "111"},
        {"key2", "222"},
        {"key3", "333"}
    };
    redis.mset(data.begin(), data.end());

    // 批量获取(包含存在的和不存在的键)
    vector<sw::redis::OptionalString> results;
    auto inserter = std::back_inserter(results);
    redis.mget({"key1", "key2", "key3", "key4"}, inserter);

    // 打印结果(不存在的键返回空Optional)
    printContainerOptional(results);  // 假设工具函数支持Optional打印
}

/* 测试字符串范围操作 */
void test6(Redis& redis) {
    std::cout << "\n=== 字符串范围操作 ===" << std::endl;
    redis.flushall();

    redis.set("key", "abcdefghijk");
    
    // 获取子字符串(闭区间,索引从0开始)
    string substr = redis.getrange("key", 2, 5);
    cout << "2-5子串: " << substr << endl;  // 输出cdef

    // 修改指定位置字符串(从索引2开始替换)
    redis.setrange("key", 2, "xyz");
    
    if (auto val = redis.get("key")) {
        cout << "修改后: " << *val << endl;  // 输出abxyzfghijk
    }
}

/* 测试自增/自减操作 */
void test7(Redis& redis) {
    std::cout << "\n=== 数值操作测试 ===" << std::endl;
    redis.flushall();

    redis.set("key", "100");
    
    // 自增操作(返回新值)
    long long new_val = redis.incr("key");
    cout << "自增后: " << new_val << endl;  // 101

    // 自减操作
    new_val = redis.decr("key");
    cout << "自减后: " << new_val << endl;  // 100

    // 验证最终值
    if (auto val = redis.get("key")) {
        cout << "最终值: " << *val << endl;
    }
}

int main() {
    try {
        // 创建Redis连接(默认端口6379)
        Redis redis("tcp://127.0.0.1:6379");
        
        // 执行测试用例(按需取消注释)
        // test1(redis);  // 基础操作
        // test2(redis);  // 过期时间
        // test3(redis);  // 条件更新
        // test4(redis);  // 批量设置
        // test5(redis);  // 批量获取
        // test6(redis);  // 范围操作
        test7(redis);    // 数值操作
        
    } catch (const std::exception& e) {
        std::cerr << "Redis操作异常: " << e.what() << endl;
        return EXIT_FAILURE;
    }
    return 0;
}

hash.cc

#include <iostream>
#include <vector>
#include <string>
#include <unordered_map>
#include <sw/redis++/redis++.h>  // Redis++ 客户端库

#include "util.hpp"              // 自定义工具函数

using std::cout;
using std::endl;
using std::vector;
using std::string;
using std::unordered_map;

using sw::redis::Redis;          // Redis 客户端对象

/* 测试 HSET 和 HGET 命令的基本用法 */
void test1(Redis& redis) {
    std::cout << "\n=== hset/hget 操作测试 ===" << std::endl;
    redis.flushall();  // 清空当前数据库(测试环境专用)

    // 单字段插入方式
    redis.hset("key", "f1", "111");  // 插入字段 f1=111
    
    // 使用 pair 对象插入
    redis.hset("key", std::make_pair("f2", "222"));

    // 初始化列表批量插入多个字段(C++11特性)
    redis.hset("key", {
        std::make_pair("f3", "333"),
        std::make_pair("f4", "444")
    });

    // 通过迭代器范围批量插入
    vector<std::pair<string, string>> fields = {
        {"f5", "555"},
        {"f6", "666"}
    };
    redis.hset("key", fields.begin(), fields.end());

    // 获取单个字段值(返回 optional<string> 类型)
    auto result = redis.hget("key", "f3");
    if (result) {  // 检查值是否存在
        std::cout << "获取字段 f3: " << result.value() << std::endl;
    } else {
        std::cout << "字段 f3 不存在" << std::endl;
    }
}

/* 测试 HEXISTS 命令判断字段存在性 */
void test2(Redis& redis) {
    std::cout << "\n=== hexists 操作测试 ===" << std::endl;
    redis.flushall();

    redis.hset("key", "f1", "111");
    redis.hset("key", "f2", "222");
    redis.hset("key", "f3", "333");

    // 判断字段是否存在(返回 bool 类型)
    bool exists = redis.hexists("key", "f4");
    std::cout << "字段 f4 存在性: " << std::boolalpha << exists << std::endl;
}

/* 测试 HDEL 和 HLEN 命令 */
void test3(Redis& redis) {
    std::cout << "\n=== hdel/hlen 操作测试 ===" << std::endl;
    redis.flushall();

    redis.hset("key", "f1", "111");
    redis.hset("key", "f2", "222");
    redis.hset("key", "f3", "333");

    // 删除单个字段(返回成功删除的字段数量)
    long long del_cnt = redis.hdel("key", "f1");
    std::cout << "删除单个字段数量: " << del_cnt << std::endl;

    // 批量删除多个字段(通过初始化列表)
    del_cnt = redis.hdel("key", {"f2", "f3"});
    std::cout << "批量删除字段数量: " << del_cnt << std::endl;

    // 获取哈希表当前字段数量
    long long remain = redis.hlen("key");
    std::cout << "剩余字段数量: " << remain << std::endl;
}

/* 测试 HKEYS 和 HVALS 命令 */
void test4(Redis& redis) {
    std::cout << "\n=== hkeys/hvals 操作测试 ===" << std::endl;
    redis.flushall();

    redis.hset("key", "f1", "111");
    redis.hset("key", "f2", "222");
    redis.hset("key", "f3", "333");

    // 获取所有字段名(通过输出迭代器)
    vector<string> fields;
    redis.hkeys("key", std::back_inserter(fields));  // 结果存入 vector
    std::cout << "所有字段: ";
    printContainer(fields);  // 假设有打印容器的工具函数

    // 获取所有字段值(注意:可能包含空字符串)
    vector<string> values;
    redis.hvals("key", std::back_inserter(values));
    std::cout << "所有值: ";
    printContainer(values);
}

/* 测试 HMSET 和 HMGET 命令 */
void test5(Redis& redis) {
    std::cout << "\n=== hmset/hmget 操作测试 ===" << std::endl;
    redis.flushall();

    // 初始化列表批量设置字段
    redis.hmset("key", {
        {"f1", "111"},
        {"f2", "222"},
        {"f3", "333"}
    });

    // 通过迭代器范围批量设置字段
    vector<std::pair<string, string>> pairs = {
        {"f4", "444"},
        {"f5", "555"},
        {"f6", "666"}
    };
    redis.hmset("key", pairs.begin(), pairs.end());

    // 批量获取多个字段的值(按请求顺序返回)
    vector<string> values;
    redis.hmget("key", {"f1", "f2", "f3"}, std::back_inserter(values));
    std::cout << "批量获取结果: ";
    printContainer(values);  // 输出可能包含空字符串(字段不存在时)
}

int main() {
    try {
        // 创建 Redis 连接(默认端口 6379)
        Redis redis("tcp://127.0.0.1:6379");
        
        // 执行测试函数(按需取消注释)
        test1(redis);  // 基础操作
        // test2(redis);  // 存在性判断
        // test3(redis);  // 删除操作
        // test4(redis);  // 遍历操作
        // test5(redis);  // 批量操作
        
    } catch (const std::exception& e) {
        std::cerr << "Redis 操作异常: " << e.what() << std::endl;
        return EXIT_FAILURE;
    }
    return 0;
}

list.cc

注意事项放在代码下面了。

#include <iostream>
#include <vector>
#include <string>
#include <unordered_map>
#include <thread>
#include <chrono>
#include <sw/redis++/redis++.h>  // Redis++ 客户端库

#include "util.hpp"              // 自定义工具函数

using std::cout;
using std::endl;
using std::vector;
using std::string;
using std::unordered_map;
using sw::redis::Redis;          // Redis 客户端对象

using namespace std::chrono_literals;  // 时间字面量支持

/* 测试 LPUSH 和 LRANGE 命令 */
void test1(Redis& redis) {
    std::cout << "\n=== LPUSH/LRANGE 操作测试 ===" << std::endl;
    redis.flushall();  // 清空数据库(仅限测试环境)

    // 从列表左侧插入单个元素
    redis.lpush("key", "111");

    // 使用初始化列表从左侧批量插入元素
    redis.lpush("key", {"222", "333", "444"});

    // 使用迭代器范围从左侧批量插入元素
    vector<string> values = {"555", "666", "777"};
    redis.lpush("key", values.begin(), values.end());

    // 获取列表中的所有元素(索引范围 0 到 -1 表示全部)
    vector<string> results;
    auto it = std::back_inserter(results);  // 插入迭代器
    redis.lrange("key", 0, -1, it);

    // 打印列表内容
    printContainer(results);
}

/* 测试 RPUSH 命令 */
void test2(Redis& redis) {
    std::cout << "\n=== RPUSH 操作测试 ===" << std::endl;
    redis.flushall();

    // 从列表右侧插入单个元素
    redis.rpush("key", "111");

    // 使用初始化列表从右侧批量插入元素
    redis.rpush("key", {"222", "333", "444"});

    // 使用迭代器范围从右侧批量插入元素
    vector<string> values = {"555", "666", "777"};
    redis.rpush("key", values.begin(), values.end());

    // 获取列表中的所有元素
    vector<string> results;
    auto it = std::back_inserter(results);
    redis.lrange("key", 0, -1, it);

    // 打印列表内容
    printContainer(results);
}

/* 测试 LPOP 和 RPOP 命令 */
void test3(Redis& redis) {
    std::cout << "\n=== LPOP/RPOP 操作测试 ===" << std::endl;
    redis.flushall();

    // 初始化测试数据
    redis.rpush("key", {"1", "2", "3", "4"});

    // 从列表左侧弹出元素(返回 optional<string>)
    auto lpop_result = redis.lpop("key");
    if (lpop_result) {
        std::cout << "LPOP 结果: " << *lpop_result << endl;
    }

    // 从列表右侧弹出元素
    auto rpop_result = redis.rpop("key");
    if (rpop_result) {
        std::cout << "RPOP 结果: " << *rpop_result << endl;
    }
}

/* 测试 BLPOP 命令 */
void test4(Redis& redis) {
    using namespace std::chrono_literals;
    std::cout << "\n=== BLPOP 操作测试 ===" << std::endl;
    redis.flushall();

    // 阻塞式弹出元素(等待最多 10 秒)
    auto result = redis.blpop({"key", "key2", "key3"}, 10s);
    if (result) {
        std::cout << "弹出键: " << result->first << endl;
        std::cout << "弹出值: " << result->second << endl;
    } else {
        std::cout << "操作超时或列表为空" << endl;
    }
}

/* 测试 LLEN 命令 */
void test5(Redis& redis) {
    std::cout << "\n=== LLEN 操作测试 ===" << std::endl;
    redis.flushall();

    // 初始化测试数据
    redis.lpush("key", {"111", "222", "333", "444"});

    // 获取列表长度
    long long len = redis.llen("key");
    std::cout << "列表长度: " << len << endl;
}

int main() {
    try {
        // 创建 Redis 连接(默认端口 6379)
        Redis redis("tcp://127.0.0.1:6379");

        // 执行测试用例(按需取消注释)
        // test1(redis);  // 左侧插入和范围查询
        // test2(redis);  // 右侧插入
        // test3(redis);  // 弹出元素
        // test4(redis);  // 阻塞式弹出
        test5(redis);    // 获取列表长度

    } catch (const std::exception& e) {
        std::cerr << "Redis 操作异常: " << e.what() << endl;
        return EXIT_FAILURE;
    }
    return 0;
}

注意事项:

        redis-plus-plus 这个库的接口风格设计非常统一。当一个函数参数需要传递多个值的时候,往往都是支持初始化列表或者是一对迭代器的方式,来进行实现的。当一个函数的返回值需要表示多个数据的时候,也往往会借助插入迭代器,来实现往一个容器中添加元素的效果。当某些场景涉及到无效值时,往往会搭配 std::optional 来进行使用。

lpush 是头插,所以每次新的元素都是插入到 list 开头,越后来的元素就越是排在前面。

optional 里面包裹着一个 pair,pair 里面是 string。

blpop 要返回的内容是两个部分:

  1. 这个元素从属于哪个 list(blpop 可以同时监听 list)。
  2. 当前被删除的元素。

        blpop 可以设定超时时间。如果在指定时间内,还没有其它客户端往指定的 list 中插入元素,此时 blpop 就直接返回了,此时返回的就是一个无效值。

对于 std::optional 类型来说,可以直接使用 -> 来访问 optional 内部包含的元素的成员变量。


set.cc

#include <iostream>
#include <vector>
#include <string>
#include <set>
#include <unordered_map>
#include <sw/redis++/redis++.h>  // Redis++ 客户端库

#include "util.hpp"              // 自定义工具函数

using std::cout;
using std::endl;
using std::vector;
using std::string;
using std::unordered_map;
using std::set;

using sw::redis::Redis;          // Redis 客户端对象

/* 测试 SADD 和 SMEMBERS 命令 */
void test1(Redis& redis) {
    std::cout << "\n=== SADD/SMEMBERS 操作测试 ===" << std::endl;
    redis.flushall();  // 清空数据库(仅限测试环境)

    // 添加单个元素到集合
    redis.sadd("key", "111");

    // 使用初始化列表批量添加元素
    redis.sadd("key", {"222", "333", "444"});

    // 使用迭代器范围批量添加元素
    set<string> elems = {"555", "666", "777"};
    redis.sadd("key", elems.begin(), elems.end());

    // 获取集合中的所有元素
    vector<string> result;
    auto it = std::inserter(result, result.end());  // 插入迭代器
    redis.smembers("key", it);

    // 打印结果(集合元素无序)
    printContainer(result);
}

/* 测试 SISMEMBER 命令 */
void test2(Redis& redis) {
    std::cout << "\n=== SISMEMBER 操作测试 ===" << std::endl;
    redis.flushall();

    // 初始化测试数据
    redis.sadd("key", {"111", "222", "333", "444"});

    // 检查元素是否存在(返回 bool 类型)
    bool exists = redis.sismember("key", "555");
    std::cout << "元素 555 存在性: " << std::boolalpha << exists << endl;
}

/* 测试 SCARD 命令 */
void test3(Redis& redis) {
    std::cout << "\n=== SCARD 操作测试 ===" << std::endl;
    redis.flushall();

    // 初始化测试数据
    redis.sadd("key", {"111", "222", "333"});

    // 获取集合中元素的数量
    long long count = redis.scard("key");
    std::cout << "集合元素数量: " << count << endl;
}

/* 测试 SPOP 命令 */
void test4(Redis& redis) {
    std::cout << "\n=== SPOP 操作测试 ===" << std::endl;
    redis.flushall();

    // 初始化测试数据
    redis.sadd("key", {"111", "222", "333", "444"});

    // 随机弹出一个元素(返回 optional<string>)
    auto result = redis.spop("key");
    if (result) {
        std::cout << "弹出元素: " << *result << endl;
    } else {
        std::cout << "集合为空" << endl;
    }
}

/* 测试 SINTER 命令 */
void test5(Redis& redis) {
    std::cout << "\n=== SINTER 操作测试 ===" << std::endl;
    redis.flushall();

    // 初始化两个集合
    redis.sadd("key1", {"111", "222", "333"});
    redis.sadd("key2", {"111", "222", "444"});

    // 计算两个集合的交集
    set<string> intersection;
    auto it = std::inserter(intersection, intersection.end());
    redis.sinter({"key1", "key2"}, it);

    // 打印交集结果
    std::cout << "交集结果: ";
    printContainer(intersection);
}

/* 测试 SINTERSTORE 命令 */
void test6(Redis& redis) {
    std::cout << "\n=== SINTERSTORE 操作测试 ===" << std::endl;
    redis.flushall();

    // 初始化两个集合
    redis.sadd("key1", {"111", "222", "333"});
    redis.sadd("key2", {"111", "222", "444"});

    // 计算交集并存储到新集合 key3
    long long count = redis.sinterstore("key3", {"key1", "key2"});
    std::cout << "交集元素数量: " << count << endl;

    // 获取并打印新集合的内容
    set<string> result;
    auto it = std::inserter(result, result.end());
    redis.smembers("key3", it);
    std::cout << "新集合内容: ";
    printContainer(result);
}

int main() {
    try {
        // 创建 Redis 连接(默认端口 6379)
        Redis redis("tcp://127.0.0.1:6379");

        // 执行测试用例(按需取消注释)
        // test1(redis);  // 添加和获取集合元素
        // test2(redis);  // 检查元素存在性
        // test3(redis);  // 获取集合大小
        // test4(redis);  // 随机弹出元素
        // test5(redis);  // 计算集合交集
        test6(redis);    // 计算并存储交集

    } catch (const std::exception& e) {
        std::cerr << "Redis 操作异常: " << e.what() << endl;
        return EXIT_FAILURE;
    }
    return 0;
}

zset.cc

#include <iostream>
#include <vector>
#include <string>
#include <set>
#include <unordered_map>
#include <sw/redis++/redis++.h>  // Redis++ 客户端库

#include "util.hpp"              // 自定义工具函数

using std::cout;
using std::endl;
using std::vector;
using std::string;
using std::unordered_map;
using std::set;

using sw::redis::Redis;          // Redis 客户端对象

/* 测试 SADD 和 SMEMBERS 命令 */
void test1(Redis& redis) {
    std::cout << "\n=== SADD/SMEMBERS 操作测试 ===" << std::endl;
    redis.flushall();  // 清空数据库(仅限测试环境)

    // 添加单个元素到集合
    redis.sadd("key", "111");

    // 使用初始化列表批量添加元素
    redis.sadd("key", {"222", "333", "444"});

    // 使用迭代器范围批量添加元素
    set<string> elems = {"555", "666", "777"};
    redis.sadd("key", elems.begin(), elems.end());

    // 获取集合中的所有元素
    vector<string> result;
    auto it = std::inserter(result, result.end());  // 插入迭代器
    redis.smembers("key", it);

    // 打印结果(集合元素无序)
    printContainer(result);
}

/* 测试 SISMEMBER 命令 */
void test2(Redis& redis) {
    std::cout << "\n=== SISMEMBER 操作测试 ===" << std::endl;
    redis.flushall();

    // 初始化测试数据
    redis.sadd("key", {"111", "222", "333", "444"});

    // 检查元素是否存在(返回 bool 类型)
    bool exists = redis.sismember("key", "555");
    std::cout << "元素 555 存在性: " << std::boolalpha << exists << endl;
}

/* 测试 SCARD 命令 */
void test3(Redis& redis) {
    std::cout << "\n=== SCARD 操作测试 ===" << std::endl;
    redis.flushall();

    // 初始化测试数据
    redis.sadd("key", {"111", "222", "333"});

    // 获取集合中元素的数量
    long long count = redis.scard("key");
    std::cout << "集合元素数量: " << count << endl;
}

/* 测试 SPOP 命令 */
void test4(Redis& redis) {
    std::cout << "\n=== SPOP 操作测试 ===" << std::endl;
    redis.flushall();

    // 初始化测试数据
    redis.sadd("key", {"111", "222", "333", "444"});

    // 随机弹出一个元素(返回 optional<string>)
    auto result = redis.spop("key");
    if (result) {
        std::cout << "弹出元素: " << *result << endl;
    } else {
        std::cout << "集合为空" << endl;
    }
}

/* 测试 SINTER 命令 */
void test5(Redis& redis) {
    std::cout << "\n=== SINTER 操作测试 ===" << std::endl;
    redis.flushall();

    // 初始化两个集合
    redis.sadd("key1", {"111", "222", "333"});
    redis.sadd("key2", {"111", "222", "444"});

    // 计算两个集合的交集
    set<string> intersection;
    auto it = std::inserter(intersection, intersection.end());
    redis.sinter({"key1", "key2"}, it);

    // 打印交集结果
    std::cout << "交集结果: ";
    printContainer(intersection);
}

/* 测试 SINTERSTORE 命令 */
void test6(Redis& redis) {
    std::cout << "\n=== SINTERSTORE 操作测试 ===" << std::endl;
    redis.flushall();

    // 初始化两个集合
    redis.sadd("key1", {"111", "222", "333"});
    redis.sadd("key2", {"111", "222", "444"});

    // 计算交集并存储到新集合 key3
    long long count = redis.sinterstore("key3", {"key1", "key2"});
    std::cout << "交集元素数量: " << count << endl;

    // 获取并打印新集合的内容
    set<string> result;
    auto it = std::inserter(result, result.end());
    redis.smembers("key3", it);
    std::cout << "新集合内容: ";
    printContainer(result);
}

int main() {
    try {
        // 创建 Redis 连接(默认端口 6379)
        Redis redis("tcp://127.0.0.1:6379");

        // 执行测试用例(按需取消注释)
        // test1(redis);  // 添加和获取集合元素
        // test2(redis);  // 检查元素存在性
        // test3(redis);  // 获取集合大小
        // test4(redis);  // 随机弹出元素
        // test5(redis);  // 计算集合交集
        test6(redis);    // 计算并存储交集

    } catch (const std::exception& e) {
        std::cerr << "Redis 操作异常: " << e.what() << endl;
        return EXIT_FAILURE;
    }
    return 0;
}

整体的Makefile和util.hpp

Makefile

.PHONY:all
all:hello generic string list set hash zset

zset:zset.cc
	g++ -std=c++17 -o $@ $^ /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -pthread

hash:hash.cc
	g++ -std=c++17 -o $@ $^ /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -pthread

set:set.cc
	g++ -std=c++17 -o $@ $^ /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -pthread

list:list.cc
	g++ -std=c++17 -o $@ $^ /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -pthread

string:string.cc
	g++ -std=c++17 -o $@ $^ /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -pthread

hello:hello.cc
	g++ -std=c++17 -o $@ $^ /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -pthread

generic:generic.cc
	g++ -std=c++17 -o $@ $^ /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -pthread

.PHONY:clean
clean:
	rm hello
	rm generic
	rm string
	rm list
	rm set
	rm hash
	rm zset

util.hpp

#pragma once

#include <vector>
#include <string>
#include <iostream>

template<typename T>
inline void printContainer(const T& container) {
    for (const auto& elem : container) {
        std::cout << elem << std::endl;
    }
}

template<typename T>
inline void printContainerPair(const T& container) {
    for (auto& elem : container) {
        // 此处预期 elem 是一个 std::pair
        std::cout << elem.first << ": " << elem.second << std::endl;
    }
}

template<typename T>
inline void printContainerOptional(const T& container) {
    for (const auto& elem : container) {
        // 此处预期 elem 是一个 optional 类型的元素, 打印之前, 先判定一下, 看是否有效
        if (elem) {
            std::cout << elem.value() << std::endl;
        } else {
            std::cout << "元素无效" << std::endl;
        }
    }
}

本篇完。

下一篇是Redis存储⑨Redis的持久化_RDB和AOF。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GR鲸鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值