文章目录
- 前言
- 使用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-plus mkdir build cd build cmake .. make make 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::Redis redis("tcp://127.0.0.1:6379");//创建一个redis对象
—–下面的接口都是拿这个对象举例!
string result = redis.ping();//ping一下,如果返回的是PONG,就说明通信没问题
清空所有数据库的全部数据:redis.flushall()–进行单元测试的时候,清除数据的操作放在开始好些(日常情况下flushall千万不要用!!!)
检查键是否存在:
exists:
auto ret = redis.exists("key");
auto ret = redis.exists({"key", "key2", "key3"});
删除键:
auto ret = redis.del("key");
auto ret = redis.del({"key", "key2", "key3"});
keys:
redis.keys(需要查找啥, 输出迭代器);
eg:
vector<string> result;
auto it = std::back_inserter(result);
redis.keys("*", it);
这里使用迭代器进行插入的原因:进行解耦合
expire:给key设置过期时间
redis.expire("key", std::chrono::seconds(10));
//如果用了using namespace std::chrono_literals,那这直接写10s就行了!
ttl:获取剩余过期时间
auto time = 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,mset和get,mget的用法:
redis.set("key", "111");
//这里的"key"和"111"其实是Redis自己封装的一个sw::redis::StringView类型(C++17的话是std::string::view的别名)
auto value = redis.get("key");//返回值是OptionalString类型.失败的话会返回这个类型的空对象
// optional 可以隐式转成 bool 类型, 可以直接在 if 中判定. 如果是无效元素, 就是返回 false
value.value()里面存的就是"111"
redis.set("key", "111", std::chrono::seconds(10));//在设置这组值的同时设置10秒过期时间
long long time = redis.ttl("key");//查询还有多久过期
auto ret = redis.exists("key");//检查key是否存在
// set 的重载版本中, 没有单独提供 NX 和 XX 的版本, 必须搭配过期时间的版本来使用.
redis.set("key", "222", 0s, sw::redis::UpdateType::EXIST);//对应的是XX
redis.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;
auto it = std::back_inserter(result);//自动调用容器的 push_back 方法插入元素
//下面这个操作返回结果的顺序和传入的 key 顺序完全一致,不存在的 key 对应位置会是 nullopt
redis.mget({"key1", "key2", "key3", "key4"}, it);
getrange和setrange:
string result = redis.getrange("key", 2, 5);//读取 key 对应字符串中,从索引2到5的子串;
redis.setrange("key", 2, "xyz");//从索引 2 的位置开始,用 "xyz" 覆盖原字符串的内容;
incr和decr:
long long result = redis.incr("key");
result = redis.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"});
// 插入一组元素, 基于迭代器
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;
auto it = std::back_inserter(results);
redis.lrange("key", 0, –1, it);
//lpop和rpop的用法
auto result = redis.lpop("key");
auto result = redis.rpop("key");
//blpop和brpop的用法:
auto result = redis.blpop({"key", "key2", "key3"}, std::chrono::seconds(10));
auto result = redis.brpop({"key", "key2", "key3"}, std::chrono::seconds(10));
result->first里面是key result->second里面是value
注意:blpop和brpop的返回值类型是optionalstringpair(pair里面那两个元素都是string类型的,一个是key(元素来自哪个列表),一个是value)
llen的用法:
long long len = 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>会更合适
auto it = std::inserter(result, result.end());//这里从哪开始插入都没事,因为set的顺序本来就没啥意义
redis.smembers("key", it);
sismember:
bool result = redis.sismember("key", "555");
scard:
long long result = redis.scard("key");
spop:
auto result = redis.spop("key");//返回值类型是OptionalString
sinter:
set<string> result;
auto it = std::inserter(result, result.end());
redis.sinter({"key1", "key2"}, it);
sinterstore:
long long len = 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:
auto result = redis.hget("key", "f3");//返回值是OptionalString
hexists:
bool result = redis.hexists("key", "f4");
hdel:
long long result = redis.hdel("key", "f1");//删除单个字段
long long result = redis.hdel("key", {"f2", "f3"});//批量删除字段
hlen:
long long len = redis.hlen("key");
hkeys:
vector<string> fields;
auto itFields = std::back_inserter(fields);
redis.hkeys("key", itFields);
hvals:
vector<string> values;
auto itValues = 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;
auto it = 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, 就是只查询 member
vector<string> memberResults;
auto it = std::back_inserter(memberResults);
redis.zrange("key", 0, –1, it);
printContainer(memberResults);
// 指向的容器包含的是一个 pair, 里面有 string 和 double, 就是查询 member 同时带有 score
vector<std::pair<string, double>> membersWithScore;
auto it2 = std::back_inserter(membersWithScore);
redis.zrange("key", 0, –1, it2);
zcard:
long long result = redis.zcard("key");
zrem:
long long del_count = redis.zrem("key", "zhangsan");
long long del_count = redis.zrem("key", {"zhangsan", "lisi", "wangwu"});
std::vector<std::string> members = {"zhangsan", "lisi", "wangwu"};
long long del_count = redis.zrem("key", members.begin(), members.end());
zscore:
auto score = redis.zscore("key", "zhangsan");
zrank:
auto rank = redis.zrank("key", "zhaoliu");//返回值是OptionalLongLong类型的
集群相关的接口
这个就是把原来的Redis类改成用RedisCluster类就行了–其他的接口都是一样的
也就是改成用RedisCluster cluster("tcp://127.0.0.1:6379");这里面填集群中任意一个节点地址都行
网硕互联帮助中心






评论前必须登录!
注册