[Redis][C++客户端]详细讲解
DieSnowK 2024-10-03 10:05:05 阅读 60
目录
0.RESP1.前置依赖 -- hiredis2.安装 redis-plus-plus3.示例:通用命令1.Quick Start2.Get && Set3.Exists4.Keys5.Expire && TTL6.Type
4.示例:String1.Set with expire2.Set NX / XX3.Mset4.Mget5.Getrange && Setrange
5.示例:list1.Push && Range2.Pop3.blpop4.llen
6.示例:set1.Sadd && Smembers2.Sismerber3.Scard4.Spop5.Sinter6.Sinterstore
7.示例:hash1.Hset && Hget2.Hexists3.Hdel4.Hkeys && Hvals5.Hmget && Hmset
8.示例:zset1.Zadd && Zrange2.Zcard3.Zrem4.Zscore
0.RESP
RESP:Redis自定义的应用层协议
传输层基于TCP,但是和TCP又没有强耦合 优点:
简单好实现快速进行解析肉眼可读 工作流程:请求和响应之间的通信模型是一问一答的形式
客户端给服务器发一个请求(Redis 命令),服务器返回一个响应客户端和服务器只需要按照格式,构造出字符串,往<code>socket中写入或者从socket
中读取字符串,按照上述格式解析
1.前置依赖 – hiredis
redis-plus-plus
是基于hiredis
实现的,hiredis
是⼀个C语⾔实现的Redis客⼾端安装:apt install libhiredis-dev
2.安装 redis-plus-plus
C++操作Redis的库有很多,此处使用redis-plus-plus
,该库功能强大,使用简单,
Github、详细API 安装:
git clone https://github.com/sewenew/redis-plus-plus.git
cd redis-plus-plus
mkdir build && cd build
cmake .. -DCMAKE_INSTALL_PREFIX=/usr
make
make install
说明:redis-plus-plus
该库接口风格非常统一,用起来非常舒服
当一个函数,参数需要传递多个值的时候,往往都是支持初始化列表或者是一对迭代器的方式,来进行实现当一个函数,返回值需要表示多个数据的时候,也往往会接入插入迭代器,来实现往一个容器中添加元素的效果当某些场景涉及到无效值的时候,往往会搭配std::optional
来进行使用
3.示例:通用命令
1.Quick Start
main.cc
:
#include <iostream>
#include <string>
#include <sw/redis++/redis++.h>
using namespace std;
int main()
{
// 构建Redis对象的时候,在构造函数中,指定Redis服务器的地址和端口
sw::redis::Redis redis("tcp://127.0.0.1:6379");
string ret = redis.ping();
cout << ret << endl;
return 0;
}
Makefile
:
main:main.cc
g++ -std=c++17 -o $@ $^ -l redis++ -l hiredis -l pthread
.PHONY:clean
clean:
rm main
2.Get && Set
此处的redis.get()
返回的是一个optional
类型
optional
类型在C++14中,正式纳入标准库因为redis.get()
会返回nil
无效值,用std::string
不方便表现nil
,而std::string*
虽然可以用nullptr
表示无效,但是返回指针又设计内存管理问题所以综上,此处使用optional
表示"非法值"/“无效值”
void TestGetAndSet(sw::redis::Redis& redis)
{
// 清空数据库,避免之前残留的数据干扰
redis.flushall();
// 使用set设置key
redis.set("key1", "233");
redis.set("key2", "666");
redis.set("key3", "888");
// 使用get获取到key对应的value
auto value1 = redis.get("key1");
// optional 可以隐式转成 bool 类型, 可以直接在 if 中判定. 如果是无效元素, 就是返回 false
if(value1)
{
cout << "value1=" << value1.value() << endl; // sw::redis::OptionalString没有重载<<
}
auto value2 = redis.get("key2");code>
if(value2)
{
cout << "value2=" << value2.value() << endl;
}
auto value3 = redis.get("key3");code>
if (value2)
{
cout << "value3=" << value3.value() << endl;
}
auto value4 = redis.get("key4");code>
if (value4)
{
// 如果不加判断,则会在此处抛出异常 std::bad_optional_acess
cout << "value4=" << value4.value() << endl;
}
}
3.Exists
void TestExists(sw::redis::Redis &redis)
{
redis.flushall();
redis.set("key1", "111");code>
redis.set("key3", "111");
auto ret = redis.exists("key1");
cout << ret << endl;
ret = redis.exists("key2");
cout << ret << endl;
// exists()的重载形式,可以用一个初始化列表传参
ret = redis.exists({ "key1", "key2", "key3"});
cout << ret << endl;
}
4.Keys
void TestKeys(sw::redis::Redis &redis)
{
redis.flushall();
redis.set("key1", "111");
redis.set("key2", "222");
redis.set("key3", "333");
redis.set("key4", "444");
redis.set("key5", "555");
redis.set("key6", "666");
// keys 的第二个参数, 是一个 "插入迭代器". 需要先准备好一个保存结果的容器.
// 接下来再创建一个插入迭代器指向容器的位置. 就可以把 keys 获取到的结果依次通过刚才的插入迭代器插入到容器的指定位置中了.
// 插入迭代器 -> 位置 + 动作
vector<string> ret;
auto it = std::back_inserter(ret);
redis.keys("*", it);
PrintContainer(ret);
}
5.Expire && TTL
void TestExpireAndTTL(sw::redis::Redis &redis)
{
redis.flushall();
redis.set("key", "111");
// 10s -> std::chrono::seconds(10);
redis.expire("key", 10s); // 此处10s为字面值,类似于2.33f
std::this_thread::sleep_for(3s);
auto time = redis.ttl("key");
cout << time << endl;
}
6.Type
void TestType(sw::redis::Redis& redis)
{
redis.flushall();
redis.set("key1", "111");
string result = redis.type("key");
cout << "key1: " << result << endl;
redis.lpush("key2", "111");
result = redis.type("key2");
cout << "key2: " << result << endl;
redis.hset("key3", "aaa", "111");
result = redis.type("key3");
cout << "key3: " << result << endl;
redis.sadd("key4", "aaa");
result = redis.type("key4");
cout << "key4: " << result << endl;
redis.zadd("key5", "吕布", 99);
result = redis.type("key5");
cout << "key5: " << result << endl;
}
4.示例:String
1.Set with expire
void TestSetWithExpire(Redis& redis)
{
redis.flushall();
redis.set("key", "111", 10s);
std::this_thread::sleep_for(3s);
long long time = redis.ttl("key");
std::cout << "time: " << time << std::endl;
}
2.Set NX / XX
void TestSetNXAndXX(Redis &redis)
{
redis.flushall();
redis.set("key", "111");
// set 的重载版本中, 没有单独提供 NX 和 XX 的版本, 必须搭配过期时间的版本来使用
// 过期时间填0s则为永不过期
redis.set("key", "222", 0s, sw::redis::UpdateType::EXIST);
auto value = redis.get("key");
if (value)
{
std::cout << "value: " << value.value() << std::endl;
}
}
3.Mset
void TestMset(Redis &redis)
{
redis.flushall();
// 第一种写法, 使用初始化列表描述多个键值对
// redis.mset({ std::make_pair("key1", "111"), std::make_pair("key2", "222"), std::make_pair("key3", "333") });
// 第二种写法, 可以把多个键值对提前组织到容器中. 以迭代器的形式告诉 mset
vector<pair<string, string>> keys = {
{ "key1", "111"},
{ "key2", "222"},
{ "key3", "333"}
};
redis.mset(keys.begin(), keys.end());
auto value = redis.get("key1");
if (value)
{
std::cout << "value: " << value.value() << std::endl;
}
value = redis.get("key2");
if (value)
{
std::cout << "value: " << value.value() << std::endl;
}
value = redis.get("key3");
if (value)
{
std::cout << "value: " << value.value() << std::endl;
}
}
4.Mget
void TestMget(Redis &redis)
{
redis.flushall();
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);
redis.mget({ "key1", "key2", "key3", "key4"}, it);
PrintContainerOptional(result);
}
5.Getrange && Setrange
void TestRange(Redis &redis)
{
redis.flushall();
redis.set("key", "DieKSnowK");
string result = redis.getrange("key", 2, 5);
cout << "result: " << result << endl;
redis.setrange("key", 2, "xyz");
auto value = redis.get("key");
cout << "value: " << value.value() << endl;
}
5.示例:list
1.Push && Range
void TestPushAndRange(Redis &redis)
{
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());
// lrange 获取到列表中的元素
vector<string> results;
auto it = std::back_inserter(results);
redis.lrange("key", 0, -1, it);
PrintContainer(results);
}
2.Pop
void TestPop(Redis &redis)
{
redis.flushall();
// 构造一个 list
redis.rpush("key", { "1", "2", "3", "4"});
auto result = redis.lpop("key");
if (result)
{
std::cout << "lpop: " << result.value() << std::endl;
}
result = redis.rpop("key");
if (result)
{
std::cout << "rpop: " << result.value() << std::endl;
}
}
3.blpop
TIPS:对于std::optional
类型来说,可以直接使用->
访问optional
内部包含的元素的成员
void TestBlpop(Redis &redis)
{
redis.flushall();
auto result = redis.blpop({ "key", "key2", "key3"}, 10s);
// 此处可以考虑在其他redis-cli插入数据以便进一步观察效果
if (result)
{
std::cout << "key:" << result->first << std::endl;
std::cout << "elem:" << result->second << std::endl;
}
else
{
std::cout << "result 无效!" << std::endl;
}
}
4.llen
void TestLlen(Redis &redis)
{
redis.flushall();
redis.lpush("key", { "111", "222", "333", "444"});
long long len = redis.llen("key");
cout << "len: " << len << endl;
}
6.示例:set
1.Sadd && Smembers
此处要注意保存结果的方式和之前的区别
void TestSaddAndSmembers(Redis& redis)
{
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::back_inserter(result);
// 此处用来保存 smembers 的结果, 使用 set 可能更合适.
set<string> result;
// 由于此处 set 里的元素顺序是固定的. 指定一个 result.end() 或者 result.begin() 或者其他位置的迭代器, 都无所谓
auto it = std::inserter(result, result.end());
redis.smembers("key", it);
PrintContainer(result);
}
2.Sismerber
void TestSismember(Redis& redis)
{
redis.flushall();
redis.sadd("key", { "111", "222", "333", "444"});
bool result = redis.sismember("key", "555");
cout << "result: " << result << endl;
}
3.Scard
void TestScard(Redis &redis)
{
redis.flushall();
redis.sadd("key", { "111", "222", "333"});
long long result = redis.scard("key");
cout << "result: " << result << endl;
}
4.Spop
void TestSpop(Redis& redis)
{
redis.flushall();
redis.sadd("key", { "111", "222", "333", "444"});
auto result = redis.spop("key");
if (result)
{
std::cout << "result: " << result.value() << std::endl;
}
else
{
std::cout << "result 无效!" << std::endl;
}
}
5.Sinter
void TestSinter(Redis &redis)
{
redis.flushall();
redis.sadd("key1", { "111", "222", "333"});
redis.sadd("key2", { "111", "222", "444"});
set<string> result;
auto it = std::inserter(result, result.end());
redis.sinter({ "key1", "key2"}, it);
PrintContainer(result);
}
6.Sinterstore
void TestSinterstore(Redis &redis)
{
redis.flushall();
redis.sadd("key1", { "111", "222", "333"});
redis.sadd("key2", { "111", "222", "444"});
long long len = redis.sinterstore("key3", { "key1", "key2"});
cout << "len: " << len << endl;
set<string> result;
auto it = std::inserter(result, result.end());
redis.smembers("key3", it);
PrintContainer(result);
}
7.示例:hash
1.Hset && Hget
void TestHsetAndHget(Redis &redis)
{
redis.flushall();
redis.hset("key", "f1", "111");
redis.hset("key", std::make_pair("f2", "222"));
// hset 能够一次性插入多个 field-value 对
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());
auto result = redis.hget("key", "f3");
if (result)
{
std::cout << "result: " << result.value() << std::endl;
}
else
{
std::cout << "result 无效!" << std::endl;
}
}
2.Hexists
void TestHexists(Redis &redis)
{
redis.flushall();
redis.hset("key", "f1", "111");
redis.hset("key", "f2", "222");
redis.hset("key", "f3", "333");
bool result = redis.hexists("key", "f4");
std::cout << "result: " << result << std::endl;
}
3.Hdel
void TestHdel(Redis &redis)
{
redis.flushall();
redis.hset("key", "f1", "111");
redis.hset("key", "f2", "222");
redis.hset("key", "f3", "333");
long long result = redis.hdel("key", "f1");
std::cout << "result: " << result << std::endl;
result = redis.hdel("key", { "f2", "f3"});
std::cout << "result: " << result << std::endl;
long long len = redis.hlen("key");
std::cout << "len: " << len << std::endl;
}
4.Hkeys && Hvals
void TestHkeysAndHvals(Redis &redis)
{
redis.flushall();
redis.hset("key", "f1", "111");
redis.hset("key", "f2", "222");
redis.hset("key", "f3", "333");
vector<string> fields;
auto itFields = std::back_inserter(fields);
redis.hkeys("key", itFields);
PrintContainer(fields);
vector<string> values;
auto itValues = std::back_inserter(values);
redis.hvals("key", itValues);
PrintContainer(values);
}
5.Hmget && Hmset
void TestHmgetAndHmset(Redis &redis)
{
redis.flushall();
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());
vector<string> values;
auto it = std::back_inserter(values);
redis.hmget("key", { "f1", "f2", "f3"}, it);
PrintContainer(values);
}
8.示例:zset
1.Zadd && Zrange
zrange
支持两种主要的风格:
只查询member
, 不带score
查询member
同时带score
关键就是看插入迭代器指向的容器的类型:
指向的容器只是包含一个string
, 就是只查询member
指向的容器包含的是一个pair
, 里面有string
和 double
, 就是查询member
同时带有score
void TestZaddAndZrange(Redis &redis)
{
redis.flushall();
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());
vector<string> memberResults;
auto it = std::back_inserter(memberResults);
redis.zrange("key", 0, -1, it);
PrintContainer(memberResults);
vector<std::pair<string, double>> membersWithScore;
auto it2 = std::back_inserter(membersWithScore);
redis.zrange("key", 0, -1, it2);
PrintContainerPair(membersWithScore);
}
2.Zcard
void TestZcard(Redis &redis)
{
redis.flushall();
redis.zadd("key", "zhangsan", 90);
redis.zadd("key", "lisi", 91);
redis.zadd("key", "wangwu", 92);
redis.zadd("key", "zhaoliu", 93);
long long result = redis.zcard("key");
std::cout << "result: " << result << std::endl;
}
3.Zrem
void TestZrem(Redis &redis)
{
redis.flushall();
redis.zadd("key", "zhangsan", 90);
redis.zadd("key", "lisi", 91);
redis.zadd("key", "wangwu", 92);
redis.zadd("key", "zhaoliu", 93);
redis.zrem("key", "zhangsan");
long long result = redis.zcard("key");
std::cout << "result: " << result << std::endl;
}
4.Zscore
void TestZscore(Redis &redis)
{
redis.flushall();
redis.zadd("key", "zhangsan", 90);
redis.zadd("key", "lisi", 91);
redis.zadd("key", "wangwu", 92);
redis.zadd("key", "zhaoliu", 93);
auto score = redis.zscore("key", "zhangsan");
if (score)
{
std::cout << "score: " << score.value() << std::endl;
}
else
{
std::cout << "score 无效" << std::endl;
}
}
上一篇: 【python 报错已解决】This error originates from a subprocess, and is likely not a problem with pip
下一篇: Java毕业设计:Java广东广州景区景点售票卖票系统毕业设计源代码作品和开题报告
本文标签
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。