[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, 里面有stringdouble, 就是查询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;

}

}




声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。