深入C++与Redis的异构之美:用redis-plus-plus优雅操控键值宇宙之通用命令版!

文章目录

本篇摘要

本文详细介绍了 C++ 操作 Redis 的完整流程:从安装 Redis 客户端库(hiredisredis-plus-plus)到编译链接,再到通过代码测试 Redis 基础命令(如 set/getexistsdelkeysexpire/ttl),并讲解了迭代器、std::optional 等 C++ 特性在其中的应用,最后提供了完整测试代码,帮助开发者快速上手 Redis C++ 客户端开发。

一.如何安装C++的Redis客户端

在上篇文章,已经可以访问到对应的github了,然后只需要下载现成的,安装即可:

进行搜索找到对应仓库:

首先进行c版本对应的安装:


bash 复制代码
 apt install libhiredis-dev

然后找到我们对应新建立一个目录进行clone即可:

bash 复制代码
git clone https://github.com/sewenew/redis-plus-plus.git

下面创建个目录进行cmake解析:

安装cmake:

bash 复制代码
apt install cmake

下面进行camke(前提是必须能找到对应的 CMakeLists.txt 文件)

最后直接make等待即可:

  • 然后能发现对应目录很多之后需要链接的一些库等文件,这里可以把它移动到系统默认搜索的路径,减少麻烦:

使用:

bash 复制代码
make install

二.进行连接Redis服务端测试

首先利用VScode进行编写:

这里我们需要包含对应的对应API的文件:redis.h

下面几个规则:

  1. #include <>:在系统目录中搜索头文件。
  2. #include "":在项目目录中搜索头文件。

因此需要找到对应路径,这里可以搜索:

因为系统目录头文件就是include里,因此可以这样包含:

cpp 复制代码
#include<sw/redis++/redis.h>

下面检查下是否对应端口启动了对应redis监听:

下面编写代码:

cpp 复制代码
#include<iostream>
#include<string>
#include<sw/redis++/redis.h>
using std::string;
using std::iostream;
using std::cout;
using std::cin;


using namespace sw::redis;

int main(){



    auto redis= Redis("tcp://127.0.0.1:6379");
    auto x=redis.ping();

       cout<<x<<std::endl;
 }

关于相关api使用可以见对应官网示例(官网示例):

下面进行Makefile书写:

需要进行链接的库:

1.libhiredis.a
2.libredis++.a
3.pthread

前两个我们已经移动到系统的路径下了,因此只需表明具体文件位置即可(如果是不在系统路径需要 -L 指明路径等):

下面我们用find查下位置:

find介绍:

​​find​​ 是 Linux/Unix 系统中一个强大的 ​​文件搜索工具​​,用于在 ​​指定目录及其子目录​​ 下递归查找符合特定条件的文件或目录。

​​ 基本语法​​:

bash 复制代码
find [查找路径] [查找条件] [处理动作]

如:

bash 复制代码
find  /usr/ -name 'libhiredis*'
  • 查找路径​​:指定从哪个目录开始搜索(默认当前目录 .)。

  • 查找条件​​:定义搜索规则(如文件名、类型、大小、时间等)。

  • 处理动作​​:对找到的文件执行的操作(如打印、删除等,默认是 -print显示文件名)。

对应Makefile:

cpp 复制代码
halo: halo.cc
	g++ halo.cc -std=c++17 -o halo   /usr/local/lib/libredis++.a  /usr/lib/x86_64-linux-gnu/libhiredis.a   -pthread
.PHONY:clean
clean:
	rm halo

注:因为这里链接前两个库的顺序不嫩颠倒,因为最后一个库包含前一个库的使用。

执行成功:

三.C++客户端使用通用命令

基于set与get简单测试

下面编写代码进行测试:

对应代码:

cpp 复制代码
#include <iostream>
#include <string>
#include <sw/redis++/redis.h>
using std::cin;
using std::cout;
using std::endl;
using std::iostream;
using std::string;

using namespace sw::redis;

int main()
{

    auto redis = Redis("tcp://127.0.0.1:6379");
     redis.flushall();
    auto ok1 = redis.set("key1", "111");

    auto val1 = redis.get("key1");
   if(val1.has_value()) cout << val1.value() << endl;

    auto ok2 = redis.set("key2", "222");

    auto val2 = redis.get("key2");
    if(val2.has_value())  cout << val2.value() << endl;

    auto val = redis.get("key3");
   if(val.has_value())   cout << val.value() << endl;
}

测试效果:

注:对应set使用的时候就当输入key+value使用,而对应get得到返回是一个对象,先要判断里面有没有值,然后再操作。

其他要点事项

1. Redis 字符串交互的类型选择
  • std::string:可修改(既能读也能写)。
  • std::string_view(C++17 引入):只读 视图,针对 "只读操作" 做了很多优化,效率比 std::string 更高。
  • 项目中也常自己封装类似 sw::redis::OptionalString 这样的类型,用来更灵活地处理 "可能为空" 的场景。
2. std::optional 的作用与使用

std::optional 用来表示 "可能有值,也可能没有(无效值 / nil)" 的语义:

  • 直接用 std::string 难以直观表达 "空值"(nullptr 本身不是 string 类型),而 optional 可以显式表示 "有值" 或 "无值"。
  • 不需要为 optional 重载 << 运算符,只需取出内部元素再输出即可(它更像一个 "单元素容器")。
3. 实际代码中的问题与处理
  • 访问 optional 内部元素时,若 optional 处于 "无效状态(无值)",会触发 std::bad_optional_access 异常(类似图中 "SIGABRT" 这类程序崩溃)。
  • 处理思路:
    • 显式 catch 异常;
    • 在访问前先判断 optional 是否有值(has_value()),避免非法访问。

基于exists简单测试

测试代码:

cpp 复制代码
void test_exist(Redis &redis)
{
    redis.flushall();

    auto ok1 = redis.set("key1", "111");
    auto ok2 = redis.set("key2", "222");
    auto ex1 = redis.exists("key3");
    cout << "key3  exist: " << ex1 << endl;
    auto ex2 = redis.exists({"key1", "key2", "key3"});
    cout << "key1 key2 key3 exist: " << ex2 << endl;
}

测试结果:


  • 返回对应存在的个数,对应的initializer,因此支持花括号。

有个疑问,对应的flushall是放在代码结尾还是开头?

答: 单元测试里 Redis 清理数据,放开始更好

  • 放结束若中途报错,清理代码可能没执行,下次测试会受残留数据影响;
  • 放开始能保证每次测试环境"干净",还方便中途检查数据。

基于del简单测试

测试代码:

cpp 复制代码
void test_del(Redis &redis)
{
    redis.flushall();

    auto ok1 = redis.set("key1", "111");
    auto ok2 = redis.set("key2", "222");
    long long d1 = redis.del("key1");
    long long d2 = redis.del("key2");
    auto ex1 = redis.exists("key3");
    cout << "key3  exist: " << ex1 << endl;
    auto ex2 = redis.exists({"key1", "key2", "key3"});
    cout << "key1 key2 key3 exist: " << ex2 << endl;
}

测试效果:

基于keys匹配操作简单测试

测试代码:

cpp 复制代码
void test_keys(Redis &redis)
{
        redis.flushall();
    auto ok2 = redis.set("key2", "111");
    auto ok3 = redis.set("key3", "111");
    auto ok4 = redis.set("key4", "111");
    auto ok5 = redis.set("key5", "111");
    vector<string> v;
   auto  bit=std::back_inserter(v);
    redis.keys("key*",bit);
    print(v);
    v.clear();
    redis.keys("*5",bit);
    print(v);

}

测试效果:

这里用到了对应的迭代器,需要我们手动传递进去,然后对应内部会填充好对应容器。

1. 迭代器是啥以及分类?

可以把迭代器理解成"指针加强版",用来遍历容器(比如数组、链表、vector这些)。不同迭代器"能力"不一样,能干的活也不一样:

  • 最弱的只能读/写一个元素(输入/输出迭代器);
  • 强点的能往前跳着读(前向迭代器);
  • 再强的能前后跳(双向迭代器);
  • 最强的能直接跳到任意位置(随机访问迭代器,比如vector就支持)。

不同的迭代器决定了你能用哪些STL算法(比如sort就要求"随机访问迭代器",因为得随便跳位置排序)。

2. 插入迭代器:专门用来"插元素"的迭代器

普通迭代器只是"指位置",插入迭代器 = 位置 + "往哪插"的动作。STL给了3种插入方式:

  • front_insert_iterator:往容器最开头 插(容器得支持push_front,比如list、deque);
  • back_insert_iterator:往容器最后面 插(容器得支持push_back,比如vector、list、deque);
  • insert_iterator:往容器任意位置前插(得指定插哪)。

3. 插入迭代器咋用?

不用自己手动写,C++给了3个辅助函数,直接调用就行:

  • front_inserter(容器) → 生成"往开头插"的迭代器;
  • back_inserter(容器) → 生成"往末尾插"的迭代器;
  • inserter(容器, 位置) → 生成"往指定位置前插"的迭代器。

插入迭代器的关键是=操作:给它赋值=,就会把值插到对应位置 。比如用back_inserter时,it = 值 就相当于容器.push_back(值)

4. 为啥要搞插入迭代器?------解耦合!

直接操作容器(比如push_back)不够灵活。插入迭代器让"算法(比如排序、拷贝)"和"容器(比如vector、list)"分开------算法只管用迭代器操作,不用关心容器具体咋插元素。这样同一个算法能适配不同容器,代码更通用。

一句话概括:插入迭代器是STL里专门帮我们往容器里"插元素"的工具,用辅助函数创建,赋值就插,还能让算法和容器解耦,写代码更省心。

基于expire与ttl简单测试

测试代码:

cpp 复制代码
void test_expire_ttl(Redis &redis)
{
    redis.flushall();
    auto ok5 = redis.set("key5", "111");
    // redis.expire("key5",std::chrono::seconds(10));
    using namespace std::chrono_literals;
    redis.expire("key5", 10s);
    // 进行对进程休眠,方面服务端计时:
    std::this_thread::sleep_for(3s);
    auto t = redis.ttl("key5");
    cout << t << endl;
}

测试效果:

这里对于传递时间两种格式:

第一种:

cpp 复制代码
#include<chrono>
redis.expire("key5",std::chrono::seconds(10));

第二种:

cpp 复制代码
#include<chrono>
using namespace std::chrono_literals;
 redis.expire("key5", 10s);

注:

  • Linux 与 Windows 的系统休眠函数不同 :在 Linux 下常用 sleep(s) 实现以秒为单位的休眠;在 Windows 下则是 Sleep(ms)(注意 Windows 下 Sleep 首字母大写,且参数单位是毫秒)。这体现了"系统函数与系统强相关,相同功能在不同系统可能对应完全不同的函数实现"这一特点。

  • 使用标准库 <thread> 中的 sleep_for :相比于依赖特定系统的函数,C++ 标准库提供的 thread 类里的 sleep_for 是更优选择。使用时需要先包含头文件 #include <thread>,示例代码为 sleep_for(3s) ,这里 3s 是 C++ 里支持的字面值常量(如算法常见的500000l1.1f 等等),表示延迟 3 秒;如果想要延迟 3 毫秒,也可以写成 3ms 这种字面值形式。

测试总代码

cpp 复制代码
#include <iostream>
#include <string>
#include <vector>
#include <sw/redis++/redis.h>
#include <chrono>
#include <thread>
using std::cin;
using std::cout;
using std::endl;
using std::iostream;
using std::string;
using std::vector;

using namespace sw::redis;

template <class T>
void print(T &arr)
{

    for (auto it : arr)
    {
        cout << it << endl;
    }
}

void test_get_set(Redis &redis)
{

    redis.flushall();

    auto ok1 = redis.set("key1", "111");

    auto val1 = redis.get("key1");
    if (val1.has_value())
        cout << val1.value() << endl;

    auto ok2 = redis.set("key2", "222");

    auto val2 = redis.get("key2");
    if (val2.has_value())
        cout << val2.value() << endl;

    auto val = redis.get("key3");
    if (val.has_value())
        cout << val.value() << endl;
}

void test_exist(Redis &redis)
{
    redis.flushall();

    auto ok1 = redis.set("key1", "111");
    auto ok2 = redis.set("key2", "222");
    auto ex1 = redis.exists("key3");
    cout << "key3  exist: " << ex1 << endl;
    auto ex2 = redis.exists({"key1", "key2", "key3"});
    cout << "key1 key2 key3 exist: " << ex2 << endl;
}

void test_del(Redis &redis)
{
    redis.flushall();

    auto ok1 = redis.set("key1", "111");
    auto ok2 = redis.set("key2", "222");
    long long d1 = redis.del("key1");
    long long d2 = redis.del("key2");
    auto ex1 = redis.exists("key3");
    cout << "key3  exist: " << ex1 << endl;
    auto ex2 = redis.exists({"key1", "key2", "key3"});
    cout << "key1 key2 key3 exist: " << ex2 << endl;
}

void test_keys(Redis &redis)
{
    redis.flushall();
    auto ok2 = redis.set("key2", "111");
    auto ok3 = redis.set("key3", "111");
    auto ok4 = redis.set("key4", "111");
    auto ok5 = redis.set("key5", "111");
    vector<string> v;
    auto bit = std::back_inserter(v);
    redis.keys("key*", bit);
    print(v);
    v.clear();
    redis.keys("*5", bit);
    print(v);
}

void test_expire_ttl(Redis &redis)
{
    redis.flushall();
    auto ok5 = redis.set("key5", "111");
    // redis.expire("key5",std::chrono::seconds(10));
    using namespace std::chrono_literals;
    redis.expire("key5", 10s);
    // 进行对进程休眠,方面服务端计时:
    std::this_thread::sleep_for(3s);
    auto t = redis.ttl("key5");
    cout << t << endl;
}

int main()

{
    Redis redis("tcp://127.0.0.1:6379");
    redis.command("select", 6);

    // test_get_set(redis);
    // test_exist(redis);

    // test_del(redis);

    // test_keys(redis);
    test_expire_ttl(redis);
}

四.本篇小结

本文用 C++ 连接 Redis:先安装客户端库(hiredis + redis-plus-plus),再通过代码测试 set/getexistsdelkeysexpire/ttl 等命令,结合 std::optional、迭代器等 C++ 特性解析常见问题,最后给出完整可运行代码,助你轻松掌握 Redis C++ 开发!

相关推荐
技术夹缝寻乐1 小时前
openEuler调度器混合负载性能深度测评
redis·mysql
Xyz996_1 小时前
Redis数据库基础
数据库·redis·缓存
MSTcheng.1 小时前
【C++】菱形继承为何会引发二义性?虚继承如何破解?
开发语言·c++
山南有清风1 小时前
基于Redis的分布式任务调用框架实现
数据库·redis·分布式·分布式任务
Lion Long1 小时前
C++20 异步编程:用future、promise 还是协程?
开发语言·c++·stl·c++20
渡我白衣1 小时前
计算机组成原理(3):计算机软件
java·c语言·开发语言·jvm·c++·人工智能·python
qq_310658511 小时前
mediasoup源码走读(三)Node.js 控制面
c++·音视频
小龙报1 小时前
【C语言初阶】动态内存分配实战指南:C 语言 4 大函数使用 + 经典笔试题 + 柔性数组优势与内存区域
android·c语言·开发语言·数据结构·c++·算法·visual studio
小龙报1 小时前
【算法通关指南:算法基础篇(三)】一维差分专题:1.【模板】差分 2.海底高铁
android·c语言·数据结构·c++·算法·leetcode·visual studio