news 2026/8/25 17:03:13

全网最细 C++ Redis 开发教程:库安装 + 通用命令 + 五大类型 + 集群适配----《Hello Redis!》(4)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
全网最细 C++ Redis 开发教程:库安装 + 通用命令 + 五大类型 + 集群适配----《Hello Redis!》(4)

文章目录

  • 前言
  • 使用C++接口操作Redis
    • 安装redis-plus-plus
    • Redis相关的C++接口
    • string相关的接口
    • list相关的接口
    • set相关的接口
    • hash相关的接口
    • zset相关的接口
    • 集群相关的接口

前言

在前面的系列文章中,我们已经系统掌握了 Redis 的核心特性、五大基础数据类型(string/hash/list/set/zset)的命令用法与业务场景。但在实际的后端项目开发中,通过编程语言调用 Redis 接口实现业务逻辑,才是落地应用的关键。对于追求高性能的 C++ 后端开发而言,redis-plus-plus是基于 hiredis 封装、支持 C++11 及以上标准的主流 Redis 客户端库,接口简洁、功能完备,完美适配生产环境开发。

本文将带你从零开始,完成C++ 操作 Redis 的全流程实战:从 Ubuntu 环境下 hiredis 与 redis-plus-plus 的安装编译,到 RESP 通信协议的核心原理;从 Redis 通用命令(ping、exists、expire 等)的 C++ 接口实现,到 string、list、set、hash、zset 五大核心类型的 API 实战调用;再到 Redis 集群的适配使用、Optional类型判空、迭代器用法等关键细节。

全文以可直接运行的代码示例为核心,覆盖开发中高频使用的所有接口,帮你快速掌握 C++ 与 Redis 的集成开发,轻松将 Redis 的缓存、队列、排行榜等能力落地到实际项目中。

使用C++接口操作Redis

这里自己选择C++操作Redis的库是redis-plus-plus

使用C++接口能操作Redis的原因:

Redis自己定义了一套请求/响应格式(RESP协议);他们网络通信的应用层都是用的RESP协议,所以…

关于RESP协议的介绍:

它的优点是:1.简单好实现 2.快速进行解析 3.RESP协议的内容是肉眼可读的

虽然RESP协议是需要TCP传输的,但是跟TCP不是强耦合(RESP协议本身不依赖TCP的任何特性)

RESP协议是基于TCP的应用层协议

注意:redis-plus-plus要C++11及以上才能使用!!!

安装redis-plus-plus

这里只介绍如何在ubantu下下载

需要先安装hiredis:apt install libhiredis-dev

再下载redis-plus-plus的源码:https://github.com/sewenew/redis-plus-plus

然后使用cmake去构建:cd redis-plus-plusmkdir buildcd buildcmake ..makemake install

Redis相关的C++接口

官方API的参考手册:redis-plus-plus/src/sw/redis++/redis.h at master · sewenew/redis-plus-plus · GitHub

redis-plus-plus这个库的接口风格还是很统一的:

一个函数参数需要传递多个值时,往往都是支持初始化列表或者一对迭代器的方式来实现

一个函数返回值需要表示多个数据的时候,也往往会借助插入迭代器来实现往一个容器中添加元素的效果

当某个场景涉及到无效值时,往往会搭配std::optional来进行使用

如果放在系统目录下的话,需要包含的头文件是#include <sw/redis++/redis++.h>

makefile编译程序时记得引入库文件:redis++的静态库 hiredis的静态库 线程库

在ubantu下:redis++的静态库文件位置:/usr/local/lib/libredis++.a

hiredis的静态库文件位置:`/usr/lib/x86\_64-linux-gnu/libhiredis.a`

检测自己的C++程序能不能成功连接上本地的Redis服务器:

sw::redis::Redisredis("tcp://127.0.0.1:6379");//创建一个redis对象---下面的接口都是拿这个对象举例!string result=redis.ping();//ping一下,如果返回的是PONG,就说明通信没问题

清空所有数据库的全部数据:redis.flushall()–进行单元测试的时候,清除数据的操作放在开始好些(日常情况下flushall千万不要用!!!)

检查键是否存在:

exists:autoret=redis.exists("key");autoret=redis.exists({"key","key2","key3"});

删除键:

autoret=redis.del("key");autoret=redis.del({"key","key2","key3"});

keys:

redis.keys(需要查找啥,输出迭代器);eg:vector<string>result;autoit=std::back_inserter(result);redis.keys("*",it);

这里使用迭代器进行插入的原因:进行解耦合

expire:给key设置过期时间

redis.expire("key",std::chrono::seconds(10));//如果用了using namespace std::chrono_literals,那这直接写10s就行了!

ttl:获取剩余过期时间

autotime=redis.ttl("key");

type:获取Redis键对应的数据类型

string result=redis.type("key");

引申:有关命名空间的补充:

using sw::redis::Redis是指以后编译器见到Redis就处理成sw::redis::Redis

using namespace std::chrono_literals:那之后编译器见到这个命名空间里有的东西就会自动加上这个前缀,比如:10s就是std::chrono::seconds(10)

注意:redis-plus-plus里面的时间只认std::chrono::duration类型的!

string相关的接口

以下接口都以举例的方式说明

set,msetget,mget的用法:

redis.set("key","111");//这里的"key"和"111"其实是Redis自己封装的一个sw::redis::StringView类型(C++17的话是std::string::view的别名)autovalue=redis.get("key");//返回值是OptionalString类型.失败的话会返回这个类型的空对象// optional 可以隐式转成 bool 类型, 可以直接在 if 中判定. 如果是无效元素, 就是返回 falsevalue.value()里面存的就是"111"
redis.set("key","111",std::chrono::seconds(10));//在设置这组值的同时设置10秒过期时间longlongtime=redis.ttl("key");//查询还有多久过期autoret=redis.exists("key");//检查key是否存在
// set 的重载版本中, 没有单独提供 NX 和 XX 的版本, 必须搭配过期时间的版本来使用.redis.set("key","222",0s,sw::redis::UpdateType::EXIST);//对应的是XXredis.set("key","222",0s,sw::redis::UpdateType::NOT_EXIST);//对应的是NX
// 第一种写法redis.mset({std::make_pair("key1","111"),std::make_pair("key2","222"),std::make_pair("key3","333")});// 第二种写法vector<std::pair<string,string>>keys={{"key1","111"},{"key2","222"},{"key3","333"}};redis.mset(keys.begin(),keys.end());
vector<sw::redis::OptionalString>result;autoit=std::back_inserter(result);//自动调用容器的 push_back 方法插入元素//下面这个操作返回结果的顺序和传入的 key 顺序完全一致,不存在的 key 对应位置会是 nulloptredis.mget({"key1","key2","key3","key4"},it);

getrangesetrange:

string result=redis.getrange("key",2,5);//读取 key 对应字符串中,从索引2到5的子串;redis.setrange("key",2,"xyz");//从索引 2 的位置开始,用 "xyz" 覆盖原字符串的内容;

incrdecr:

longlongresult=redis.incr("key");result=redis.decr("key");

Redis在处理数字型key时,优先用incr或decr来轮换得到最初数字,而不是get然后手动转成数字

关于StringViewstring的区别:

StringView:相当于只是一个只读的快捷方式,string是一个完整的字符串拷贝

–使用StringView的话,性能会更好(因为减少了两次拷贝)

–而且string不好表示无效值,如果用string*的话,又不好管理内存

注意:如果用OptionalString的时候,没有先判断它有没有值就强行取值的话是会崩溃!!!–所以在访问得到的结果之前都要判空一下

引申:STL中的五种迭代器类型:1.输入迭代器 2.输出迭代器 3.前向迭代器 4.双向迭代器 5.随机访问迭代器

list相关的接口

lpush,rpushlrange,lpoprpop,blpopbrpop:

//lpush版本// 插入单个元素redis.lpush("key","111");// 插入一组元素, 基于初始化列表redis.lpush("key",{"222","333","444"});// 插入一组元素, 基于迭代器vector<string>values={"555","666","777"};redis.lpush("key",values.begin(),values.end());//rpush版本// 插入单个元素redis.rpush("key","111");// 插入多个元素, 基于初始化列表redis.rpush("key",{"444","333","222"});// 插入多个元素, 基于容器vector<string>values={"777","666","555"};redis.rpush("key",values.begin(),values.end());// lrange 获取到列表中的元素vector<string>results;autoit=std::back_inserter(results);redis.lrange("key",0,-1,it);//lpop和rpop的用法autoresult=redis.lpop("key");autoresult=redis.rpop("key");//blpop和brpop的用法:autoresult=redis.blpop({"key","key2","key3"},std::chrono::seconds(10));autoresult=redis.brpop({"key","key2","key3"},std::chrono::seconds(10));result->first里面是key result->second里面是value

注意:blpopbrpop的返回值类型是optionalstringpair(pair里面那两个元素都是string类型的,一个是key(元素来自哪个列表),一个是value)

llen的用法:

longlonglen=redis.llen("key");

set相关的接口

sadd

// 一次添加一个元素redis.sadd("key","111");// 一次添加多个元素(使用初始化列表)redis.sadd("key",{"222","333","444"});// 一次添加多个元素(使用迭代器)set<string>elems={"555","666","777"};redis.sadd("key",elems.begin(),elems.end());

smembers:

vector<string>result;//用set<string>会更合适autoit=std::inserter(result,result.end());//这里从哪开始插入都没事,因为set的顺序本来就没啥意义redis.smembers("key",it);

sismember:

boolresult=redis.sismember("key","555");

scard:

longlongresult=redis.scard("key");

spop:

autoresult=redis.spop("key");//返回值类型是OptionalString

sinter:

set<string>result;autoit=std::inserter(result,result.end());redis.sinter({"key1","key2"},it);

sinterstore:

longlonglen=redis.sinterstore("key3",{"key1","key2"});

set不能直接用back_inserter的原因:std::set没有push_back方法

hash相关的接口

hset:

redis.hset("key","f1","111");redis.hset("key",std::make_pair("f2","222"));redis.hset("key",{std::make_pair("f3","333"),std::make_pair("f4","444")});vector<std::pair<string,string>>fields={std::make_pair("f5","555"),std::make_pair("f6","666")};redis.hset("key",fields.begin(),fields.end());

hget:

autoresult=redis.hget("key","f3");//返回值是OptionalString

hexists:

boolresult=redis.hexists("key","f4");

hdel:

longlongresult=redis.hdel("key","f1");//删除单个字段longlongresult=redis.hdel("key",{"f2","f3"});//批量删除字段

hlen:

longlonglen=redis.hlen("key");

hkeys:

vector<string>fields;autoitFields=std::back_inserter(fields);redis.hkeys("key",itFields);

hvals:

vector<string>values;autoitValues=std::back_inserter(values);redis.hvals("key",itValues);

hmset:

redis.hmset("key",{std::make_pair("f1","111"),std::make_pair("f2","222"),std::make_pair("f3","333")});vector<std::pair<string,string>>pairs={std::make_pair("f4","444"),std::make_pair("f5","555"),std::make_pair("f6","666")};redis.hmset("key",pairs.begin(),pairs.end());

hmget:

vector<string>values;autoit=std::back_inserter(values);redis.hmget("key",{"f1","f2","f3"},it);

zset相关的接口

zadd:

redis.zadd("key","吕布",99);redis.zadd("key",{std::make_pair("赵云",98),std::make_pair("典韦",97)});vector<std::pair<string,double>>members={std::make_pair("关羽",95),std::make_pair("张飞",93)};redis.zadd("key",members.begin(),members.end());

zrange:

// 指向的容器只是包含一个 string, 就是只查询 membervector<string>memberResults;autoit=std::back_inserter(memberResults);redis.zrange("key",0,-1,it);printContainer(memberResults);// 指向的容器包含的是一个 pair, 里面有 string 和 double, 就是查询 member 同时带有 scorevector<std::pair<string,double>>membersWithScore;autoit2=std::back_inserter(membersWithScore);redis.zrange("key",0,-1,it2);

zcard:

longlongresult=redis.zcard("key");

zrem:

longlongdel_count=redis.zrem("key","zhangsan");longlongdel_count=redis.zrem("key",{"zhangsan","lisi","wangwu"});std::vector<std::string>members={"zhangsan","lisi","wangwu"};longlongdel_count=redis.zrem("key",members.begin(),members.end());

zscore:

autoscore=redis.zscore("key","zhangsan");

zrank:

autorank=redis.zrank("key","zhaoliu");//返回值是OptionalLongLong类型的

集群相关的接口

这个就是把原来的Redis类改成用RedisCluster类就行了–其他的接口都是一样的

也就是改成用RedisCluster cluster("tcp://127.0.0.1:6379");这里面填集群中任意一个节点地址都行

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/25 17:01:16

企业AI落地三大路径解析:做系统、搭积木、连流量

1. 项目概述&#xff1a;一场关于企业AI生存法则的深度解构最近和几个在不同大厂做AI中台和业务线的朋友聊天&#xff0c;大家不约而同地都在讨论一个话题&#xff1a;当AI的浪潮从技术狂欢转向商业落地&#xff0c;企业到底该怎么玩才能活下来&#xff0c;甚至活得更好&#x…

作者头像 李华
网站建设 2026/8/25 16:57:43

TriEx框架:用游戏化三视图破解多智能体协作黑箱

1. 项目概述&#xff1a;当大模型学会“打游戏”并“复盘”最近在折腾多智能体大语言模型&#xff08;Multi-Agent LLMs&#xff09;的可解释性&#xff0c;发现一个挺有意思的瓶颈&#xff1a;我们能让一群AI协作完成任务&#xff0c;比如写代码、做规划&#xff0c;但它们内部…

作者头像 李华
网站建设 2026/8/25 16:54:41

指针 vs 引用

旨在理解指针和引用的区别 Person a("张三");Person* p &a; //指针 Person& r a; //引用定义方式 • 指针&#xff1a;Person* p&#xff0c;p 是独立变量&#xff0c;存对象的地址&#xff0c;p 自己有内存。 • 引用&#xff1a;Person& …

作者头像 李华
网站建设 2026/8/25 16:48:40

智能体泛化难题:静态训练在开放世界工具使用中的脆弱性剖析

1. 引言&#xff1a;当智能体走出“温室”最近和几个做AI应用落地的朋友聊天&#xff0c;大家不约而同地提到了一个共同的痛点&#xff1a;在实验室里、在精心构建的测试集上表现堪称完美的智能体&#xff08;Agent&#xff09;&#xff0c;一旦部署到真实的生产环境&#xff0…

作者头像 李华
网站建设 2026/8/25 16:48:00

突破LLM上下文瓶颈:前瞻性上下文工程提升智能体长程任务表现

1. 项目缘起&#xff1a;当Agent服务遭遇长程任务的“记忆墙” 最近在折腾一个基于大语言模型的智能体项目&#xff0c;目标是让它能处理像“帮我分析过去三个月的销售数据&#xff0c;找出异常波动&#xff0c;并生成一份包含图表和建议的周报”这样的复杂长程任务。理想很丰满…

作者头像 李华
网站建设 2026/8/25 16:47:49

VMware无头模式详解:命令行启动虚拟机的实战指南

1. 什么是 VMware 无头模式&#xff1f;它为什么值得你花 5 分钟搞懂“Vmware 无头模式启动虚拟机&#xff08;不打开vmware , 直接启动虚拟机&#xff09;&#xff0c;Mac Windows 版本”——这个标题里藏着一个被大量新手忽略、却被运维、开发、测试和自动化工程师天天用的硬…

作者头像 李华