ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

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

2026/8/25 17:03:27 拓冰建站 浏览量
全网最细 C++ Redis 开发教程:库安装 + 通用命令 + 五大类型 + 集群适配----《Hello Redis!》(4) 文章目录前言使用C接口操作Redis安装redis-plus-plusRedis相关的C接口string相关的接口list相关的接口set相关的接口hash相关的接口zset相关的接口集群相关的接口前言在前面的系列文章中我们已经系统掌握了 Redis 的核心特性、五大基础数据类型string/hash/list/set/zset的命令用法与业务场景。但在实际的后端项目开发中通过编程语言调用 Redis 接口实现业务逻辑才是落地应用的关键。对于追求高性能的 C 后端开发而言redis-plus-plus是基于 hiredis 封装、支持 C11 及以上标准的主流 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要C11及以上才能使用!!!安装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 installRedis相关的C接口官方API的参考手册:redis-plus-plus/src/sw/redis/redis.h at master · sewenew/redis-plus-plus · GitHubredis-plus-plus这个库的接口风格还是很统一的:一个函数参数需要传递多个值时往往都是支持初始化列表或者一对迭代器的方式来实现一个函数返回值需要表示多个数据的时候也往往会借助插入迭代器来实现往一个容器中添加元素的效果当某个场景涉及到无效值时往往会搭配std::optional来进行使用如果放在系统目录下的话需要包含的头文件是#include sw/redis/redis.h用makefile编译程序时记得引入库文件:redis的静态库 hiredis的静态库 线程库在ubantu下:redis的静态库文件位置:/usr/local/lib/libredis.ahiredis的静态库文件位置:/usr/lib/x86\_64-linux-gnu/libhiredis.a检测自己的C程序能不能成功连接上本地的Redis服务器sw::redis::Redisredis(tcp://127.0.0.1:6379);//创建一个redis对象---下面的接口都是拿这个对象举例!string resultredis.ping();//ping一下如果返回的是PONG就说明通信没问题清空所有数据库的全部数据:redis.flushall()–进行单元测试的时候清除数据的操作放在开始好些日常情况下flushall千万不要用!!!检查键是否存在:exists:autoretredis.exists(key);autoretredis.exists({key,key2,key3});删除键:autoretredis.del(key);autoretredis.del({key,key2,key3});keys:redis.keys(需要查找啥,输出迭代器);eg:vectorstringresult;autoitstd::back_inserter(result);redis.keys(*,it);这里使用迭代器进行插入的原因:进行解耦合expire:给key设置过期时间redis.expire(key,std::chrono::seconds(10));//如果用了using namespace std::chrono_literals那这直接写10s就行了!ttl获取剩余过期时间autotimeredis.ttl(key);type:获取Redis键对应的数据类型string resultredis.type(key);引申:有关命名空间的补充:using sw::redis::Redis是指以后编译器见到Redis就处理成sw::redis::Redisusing namespace std::chrono_literals那之后编译器见到这个命名空间里有的东西就会自动加上这个前缀比如:10s就是std::chrono::seconds(10)注意:redis-plus-plus里面的时间只认std::chrono::duration类型的!string相关的接口以下接口都以举例的方式说明set,mset和get,mget的用法:redis.set(key,111);//这里的key和111其实是Redis自己封装的一个sw::redis::StringView类型(C17的话是std::string::view的别名)autovalueredis.get(key);//返回值是OptionalString类型.失败的话会返回这个类型的空对象// optional 可以隐式转成 bool 类型, 可以直接在 if 中判定. 如果是无效元素, 就是返回 falsevalue.value()里面存的就是111redis.set(key,111,std::chrono::seconds(10));//在设置这组值的同时设置10秒过期时间longlongtimeredis.ttl(key);//查询还有多久过期autoretredis.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)});// 第二种写法vectorstd::pairstring,stringkeys{{key1,111},{key2,222},{key3,333}};redis.mset(keys.begin(),keys.end());vectorsw::redis::OptionalStringresult;autoitstd::back_inserter(result);//自动调用容器的 push_back 方法插入元素//下面这个操作返回结果的顺序和传入的 key 顺序完全一致不存在的 key 对应位置会是 nulloptredis.mget({key1,key2,key3,key4},it);getrange和setrange:string resultredis.getrange(key,2,5);//读取 key 对应字符串中从索引2到5的子串redis.setrange(key,2,xyz);//从索引 2 的位置开始用 xyz 覆盖原字符串的内容incr和decr:longlongresultredis.incr(key);resultredis.decr(key);Redis在处理数字型key时优先用incr或decr来轮换得到最初数字而不是get然后手动转成数字关于StringView和string的区别:StringView:相当于只是一个只读的快捷方式string是一个完整的字符串拷贝–使用StringView的话性能会更好(因为减少了两次拷贝)–而且string不好表示无效值如果用string*的话又不好管理内存注意:如果用OptionalString的时候没有先判断它有没有值就强行取值的话是会崩溃–所以在访问得到的结果之前都要判空一下引申:STL中的五种迭代器类型:1.输入迭代器 2.输出迭代器 3.前向迭代器 4.双向迭代器 5.随机访问迭代器list相关的接口lpush,rpush和lrange,lpop和rpop,blpop和brpop://lpush版本// 插入单个元素redis.lpush(key,111);// 插入一组元素, 基于初始化列表redis.lpush(key,{222,333,444});// 插入一组元素, 基于迭代器vectorstringvalues{555,666,777};redis.lpush(key,values.begin(),values.end());//rpush版本// 插入单个元素redis.rpush(key,111);// 插入多个元素, 基于初始化列表redis.rpush(key,{444,333,222});// 插入多个元素, 基于容器vectorstringvalues{777,666,555};redis.rpush(key,values.begin(),values.end());// lrange 获取到列表中的元素vectorstringresults;autoitstd::back_inserter(results);redis.lrange(key,0,-1,it);//lpop和rpop的用法autoresultredis.lpop(key);autoresultredis.rpop(key);//blpop和brpop的用法:autoresultredis.blpop({key,key2,key3},std::chrono::seconds(10));autoresultredis.brpop({key,key2,key3},std::chrono::seconds(10));result-first里面是key result-second里面是value注意:blpop和brpop的返回值类型是optionalstringpair(pair里面那两个元素都是string类型的一个是key(元素来自哪个列表)一个是value)llen的用法:longlonglenredis.llen(key);set相关的接口sadd// 一次添加一个元素redis.sadd(key,111);// 一次添加多个元素(使用初始化列表)redis.sadd(key,{222,333,444});// 一次添加多个元素(使用迭代器)setstringelems{555,666,777};redis.sadd(key,elems.begin(),elems.end());smembers:vectorstringresult;//用setstring会更合适autoitstd::inserter(result,result.end());//这里从哪开始插入都没事因为set的顺序本来就没啥意义redis.smembers(key,it);sismember:boolresultredis.sismember(key,555);scard:longlongresultredis.scard(key);spop:autoresultredis.spop(key);//返回值类型是OptionalStringsinter:setstringresult;autoitstd::inserter(result,result.end());redis.sinter({key1,key2},it);sinterstore:longlonglenredis.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)});vectorstd::pairstring,stringfields{std::make_pair(f5,555),std::make_pair(f6,666)};redis.hset(key,fields.begin(),fields.end());hget:autoresultredis.hget(key,f3);//返回值是OptionalStringhexists:boolresultredis.hexists(key,f4);hdel:longlongresultredis.hdel(key,f1);//删除单个字段longlongresultredis.hdel(key,{f2,f3});//批量删除字段hlen:longlonglenredis.hlen(key);hkeys:vectorstringfields;autoitFieldsstd::back_inserter(fields);redis.hkeys(key,itFields);hvals:vectorstringvalues;autoitValuesstd::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)});vectorstd::pairstring,stringpairs{std::make_pair(f4,444),std::make_pair(f5,555),std::make_pair(f6,666)};redis.hmset(key,pairs.begin(),pairs.end());hmget:vectorstringvalues;autoitstd::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)});vectorstd::pairstring,doublemembers{std::make_pair(关羽,95),std::make_pair(张飞,93)};redis.zadd(key,members.begin(),members.end());zrange:// 指向的容器只是包含一个 string, 就是只查询 membervectorstringmemberResults;autoitstd::back_inserter(memberResults);redis.zrange(key,0,-1,it);printContainer(memberResults);// 指向的容器包含的是一个 pair, 里面有 string 和 double, 就是查询 member 同时带有 scorevectorstd::pairstring,doublemembersWithScore;autoit2std::back_inserter(membersWithScore);redis.zrange(key,0,-1,it2);zcard:longlongresultredis.zcard(key);zrem:longlongdel_countredis.zrem(key,zhangsan);longlongdel_countredis.zrem(key,{zhangsan,lisi,wangwu});std::vectorstd::stringmembers{zhangsan,lisi,wangwu};longlongdel_countredis.zrem(key,members.begin(),members.end());zscore:autoscoreredis.zscore(key,zhangsan);zrank:autorankredis.zrank(key,zhaoliu);//返回值是OptionalLongLong类型的集群相关的接口这个就是把原来的Redis类改成用RedisCluster类就行了–其他的接口都是一样的也就是改成用RedisCluster cluster(tcp://127.0.0.1:6379);这里面填集群中任意一个节点地址都行