【redis初阶】redis客户端
目录
一、基本介绍
二、认识RESP(redis自定的应用层协议名称)
三、访问github的技巧
四、安装redisplusplus
4.1 安装 hiredis**
4.2 下载 redis-plus-plus 源码
4.3 编译/安装 redis-plus-plus
五、编写运行helloworld
六、redis命令演示
6.1 通用命令的使用
6.2 string的使用
6.3 list的使用
6.4 set的使用
6.5 hash的使用
6.6 zset的使用
redis学习🥳
一、基本介绍
在前面的学习中主要是学习 redis 的各种基本操作/命令,都是在 redis 命令行客户端中手动执行 redis各种命令,但这种操作方式不是我们日常开发中的主要形式;更多的时候,我们是使用 redis 的 api 来实现定制化的 redis 客户端程序,进一步操作 redis 服务器;
不管是 redis 提供的命令行客户端,还是第三方的图形化客户端,他们本质上都属于是“通用的客户端程序”。但相比之下,我们在工作中更希望使用到的是“专用的”“定制化”的客户端程序;
二、认识RESP(redis自定的应用层协议名称)
我们为什么能编写出一个自定义的redis客户端呢?
我们知道在网络通信过程中,会使用到很多的“协议”
应用层往下的协议是固定好的,是在系统内核或者驱动程序中实现的,我们是不可修改的,但是对于应用层来说,虽然业界有很多成熟的应用层协议,比如 http 等,但是此处更多的时候,都会“自定义”应用层协议,redis 此处应用层协议,就是自定义的应用层协议,客户端按照 resp 协议发送请求,服务器按照这个协议进行解析,在按照这个协议构造响应,客户端在解析这个响应,完成客户端和服务器的通信;
因此:resp 就是 redis 自定义的客户端和服务器用于通信的应用层协议;
resp协议特点:
- 简单好实现;
- 快速进行解析;
- 肉眼可读;
- 基于传输层的tcp,但是与tcp又没有强耦合;
- 请求和响应之间的通信是一问一答的形式(客户端给服务器发起一个请求,服务器返回一个响应);
- 客户端给redis服务器发送命令时:是以bulk string数组的形式发送的redis命令,对于不同的redis命令,服务器返回的结果是不一样的(ok、整个数组...);
在RESP中,第一个字节决定了数据类型:
- 对于Simple Strings,回复的第一个字节是“+” eg:"+OK\r\n"
- 对于Errors,回复的第一个字节是“-” eg:"-Error message\r\n"
- 对于Integers,回复的第一个字节是“:” eg:":1000\r\n"
- 对于Bulk Strings,回复的第一个字节是“$” eg:"$5\r\nhello\r\n"
- 对于Arrays,回复的第一个字节是“*” eg:"*2\r\n$5\r\nhello\r\n$5\r\nworld\r\n"
在进行通信的时候,服务器就把构造好的字符串,写入到 tcp socket 中;
Simple Strings 和 Bulk Strings 的区别:Simple String 只能用来传输文本,Bulk String 可以传输二进制数据;
上述这套协议公开已久,已经有很多大佬,实现了这套协议的解析/构造,在写代码的时候,我们不需要按照上述的协议,解析/构造字符串,只要使用这些大佬们提供的库,就可以比较简单方便的来完成和 redis 服务器通信的操作了;
三、访问github的技巧
翻墙 or 下载游戏加速器,推荐:watt toolkit
选中gitup,一键加速
四、安装redisplusplus
C++ 操作 redis 的库有很多,咱们此处使用 redis-plus-plus.
这个库的功能强大,使用简单.
Github 地址: https://github.com/sewenew/redis-plus-plus
4.1 安装 hiredis**
redis-plus-plus 是基于 hiredis 实现的.
hiredis 是一个 C 语言实现的 redis 客户端.
因此需要先安装 hiredis. 直接使用包管理器安装即可.
在 Ubuntu 镜像下:
apt install libhiredis-dev
4.2 下载 redis-plus-plus 源码
git clone https://github.com/sewenew/redis-plus-plus.git
4.3 编译/安装 redis-plus-plus
redis-plus-plus 是使用 cmake 构建工具进行构建的,cmake 相当于是 Makefile 的升级版,Makefile 本身功能比较简陋,比较原始,写起来也比较麻烦实际开发中很少会手写 makefile,cmake 就是通过程序来生成 makefile 的 工具;
在 Ubuntu 镜像下:
使用 cmake 构建:
cd redis-plus-plus# 创建一个 build 目录是习惯做法,并非必须。目的是为了让编译生成的临时文件都放到 build 下,避免污染源代码目录
mkdir build
cd build# 这个操作是生成makefile,此处..指向的是刚才 CMakeLits.txt 文件所在的目录
cmake3 ..make # 进行编译
sudo make install # 将编译生成的 .a .so 等系列库拷贝到系统目录
make 后结果:
make install 后结果:
构建成功后,会在 /usr/local/include/ 中多出 sw 目录,并且内部包含 redis-plus-plus 的一系列头文件;
会在 /usr/local/lib/ 中多出一系列 libredis 库文件.
五、编写运行helloworld
创建代码目录:
编写 hello.cpp 文件:
#include <iostream>
#include <vector>
#include <string>
#include <unordered_map>
#include <sw/redis++/redis++.h>using std::cout;
using std::endl;
using std::vector;
using std::string;
using std::unordered_map;int main() {// 创建 Redis 对象的时候,需要在构造函数中指定 redis 服务器地址和端口;sw::redis::Redis redis("tcp://127.0.0.1:6379");// 使用 ping 方法,让客户端给服务器发送了一个 PING,然后服务器就会返回一个 PONG,通过返回值获取的string result = redis.ping();cout << result << endl;
}
编写 makefile 文件:
hello:hello.cppg++ -std=c++17 -o $@ $^ /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -pthread# /usr/local/lib/libredis++.a redis自己的静态库# /usr/lib/x86_64-linux-gnu/libhiredis.a hiredis的静态库# -pthread 线程库.PHONY:clean
clean:rm hello
编译运行:
六、redis命令演示
工具文件:
util.hpp 文件:
# pragma once# include <iostream>
# include <vector>
#include <string>// 打印容器内元素
template<typename T>
inline void printContainer(const T& container) {for(const auto& elem : container) {std::cout << elem << std::endl;}
}// 当容器内元素是 pair 类型时,打印容器内元素
template<typename T>
inline void printContainerPair(const T& container) {for(const auto& elem : container) {std::cout << elem.first << " : " << elem.second << std::endl;}
}// 当容器内元素是 optional 类型时,打印容器内元素
template<typename T>
inline void printContainerOptional(const T& container) {for(const auto& elem : container) {if(elem) {std::cout << elem.value() << std::endl;} else {std::cout << "元素无效" << std::endl;}}
}
6.1 通用命令的使用
genetric.cpp 文件:
#include <iostream>
#include <vector>
#include <string>
#include <unordered_map>
#include <chrono>
#include <thread>
#include <sw/redis++/redis++.h>#include "util.hpp"using std::cout;
using std::endl;
using std::vector;
using std::string;
using std::unordered_map;
using sw::redis::Redis;// get 和 set 的使用
void test1(Redis& redis) {cout << "get 和 set 的使用" << endl;// 清空数据库,避免之前残留的数据有干扰redis.flushall();// 使用 set 设置 keyredis.set("key1", "111");redis.set("key2", "222");redis.set("key3", "333");// 使用 get 获取到 key 对应的 valueauto value1 = redis.get("key1");if(value1) {cout << "value1 = " << value1.value() << endl;} else {cout << "当前 key 不存在" << endl;}auto value2 = redis.get("key2");if(value2) {cout << "value2 = " << value2.value() << endl;} else {cout << "当前 key 不存在" << endl;}auto value3 = redis.get("key3");if(value3) {cout << "value3 = " << value3.value() << endl;} else {cout << "当前 key 不存在" << endl;}auto value4 = redis.get("key4");if(value4) {cout << "value4 = " << value4.value() << endl;} else {cout << "当前 key 不存在" << endl;}
}// exists 的使用
void test2(Redis& redis) {cout << "exists" << endl;redis.flushall();redis.set("key", "111");redis.set("key3", "111");auto ret = redis.exists("key");cout << ret << endl;ret = redis.exists("key2");cout << ret << endl;ret = redis.exists({"key1", "key2", "key3"});cout << ret << endl;
}// del 的使用
void test3(Redis& redis) {cout << "del" << endl;redis.flushall();redis.set("key1", "111");redis.set("key2", "222");vector<string> input{"key1", "key2", "key3"};auto ret = redis.del(input.begin(), input.end());cout << ret << endl;ret = redis.exists({"key1", "key2"});cout << ret << endl;
}// keys 的使用
void test4(Redis& redis) {cout << "keys" << endl;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> result;auto it = std::back_inserter(result);redis.keys("*", it);printContainer(result);
}void test5(Redis& redis) {using namespace std::chrono_literals;std::cout << "expire and ttl" << std::endl;redis.flushall();redis.set("key", "111");// 10s => std::chrono::seconds(10)redis.expire("key", 10s);std::this_thread::sleep_for(3s);auto time = redis.ttl("key");std::cout << time << std::endl;
}void test6(sw::redis::Redis& redis) {std::cout << "type" << std::endl;redis.flushall();redis.set("key", "111");string result = redis.type("key");std::cout << "key: " << result << std::endl;redis.lpush("key2", "111");result = redis.type("key2");std::cout << "key2: " << result << std::endl;redis.hset("key3", "aaa", "111");result = redis.type("key3");std::cout << "key3: " << result << std::endl;redis.sadd("key4", "aaa");result = redis.type("key4");std::cout << "key4: " << result << std::endl;redis.zadd("key5", "吕布", 99);result = redis.type("key5");std::cout << "key5: " << result << std::endl;
}int main() {Redis redis("tcp://127.0.0.1:6379");// test1(redis);// test2(redis);// test3(redis);// test4(redis);// test5(redis);test6(redis);
}
get 和 set:test1()结果:
exists:test2()结果:
del:test3()结果:
keys:test4()结果:
expire and ttl:test5()结果:
type:test6()结果:
6.2 string的使用
string.cpp 文件:
#include <iostream>
#include <vector>
#include <string>
#include <unordered_map>
#include <chrono>
#include <thread>#include <sw/redis++/redis++.h>
#include "util.hpp"using std::cout;
using std::endl;
using std::vector;
using std::string;
using std::unordered_map;using sw::redis::Redis;using namespace std::chrono_literals;void test1(Redis& redis) {std::cout << "get 和 set" << std::endl;redis.flushall();redis.set("key", "111");auto value = redis.get("key");if (value) {std::cout << "value: " << value.value() << std::endl;}redis.set("key", "222");value = redis.get("key");if (value) {std::cout << "value: " << value.value() << std::endl;}
}void test2(Redis& redis) {std::cout << "set 带有超时时间" << std::endl;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;
}void test3(Redis& redis) {std::cout << "set NX 和 XX" << std::endl;redis.flushall();redis.set("key", "111");// set 的重载版本中, 没有单独提供 NX 和 XX 的版本, 必须搭配过期时间的版本来使用. redis.set("key", "222", 0s, sw::redis::UpdateType::EXIST);auto value = redis.get("key");if (value) {std::cout << "value: " << value.value() << std::endl;} else {std::cout << "key 不存在!" << std::endl;}
}void test4(Redis& redis) {std::cout << "mset" << std::endl;redis.flushall();// 第一种写法, 使用初始化列表描述多个键值对// redis.mset({ std::make_pair("key1", "111"), std::make_pair("key2", "222"), std::make_pair("key3", "333") });// 第二种写法, 可以把多个键值对提前组织到容器中. 以迭代器的形式告诉 msetvector<std::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;}
}void test5(Redis& redis) {std::cout << "mget" << std::endl;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);
}void test6(Redis& redis) {std::cout << "getrange 和 setrange" << std::endl;redis.flushall();redis.set("key", "abcdefghijk");string result = redis.getrange("key", 2, 5);std::cout << "result: " << result << std::endl;redis.setrange("key", 2, "xyz");auto value = redis.get("key");std::cout << "value: " << value.value() << std::endl;
}void test7(Redis& redis) {std::cout << "incr 和 decr" << std::endl;redis.flushall();redis.set("key", "100");long long result = redis.incr("key");std::cout << "result: " << result << std::endl;auto value = redis.get("key");std::cout << "value: " << value.value() << std::endl;result = redis.decr("key");std::cout << "result: " << result << std::endl;value = redis.get("key");std::cout << "value: " << value.value() << std::endl;
}int main() {Redis redis("tcp://127.0.0.1:6379");// test1(redis);// test2(redis);// test3(redis);// test4(redis);// test5(redis);// test6(redis);test7(redis);return 0;
}
get 和 set:test1()结果:
set 带有超时时间:test2()结果:
set NX 和 XX:test3()结果:
mset 带有超时时间:test4()结果:
mget 带有超时时间:test5()结果:
getrange 和 setrange 带有超时时间:test6()结果:
incr 和 dec 带有超时时间:test7()结果:
6.3 list的使用
list.cpp 文件:
#include <iostream>
#include <vector>
#include <string>
#include <unordered_map>
#include <thread>
#include <chrono>
#include <sw/redis++/redis++.h>
#include "util.hpp"using std::cout;
using std::endl;
using std::vector;
using std::string;
using std::unordered_map;
using sw::redis::Redis;using namespace std::chrono_literals;void test1(Redis& redis) {std::cout << "lpush 和 lrange" << std::endl;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);
}void test2(Redis& redis) {std::cout << "rpush" << std::endl;redis.flushall();// 插入单个元素redis.rpush("key", "111");// 插入多个元素, 基于初始化列表redis.rpush("key", {"222", "333", "444"});// 插入多个元素, 基于容器vector<string> values = {"555", "666", "777"};redis.rpush("key", values.begin(), values.end());// 使用 lrange 获取元素vector<string> results;auto it = std::back_inserter(results);redis.lrange("key", 0, -1, it);printContainer(results);
}void test3(Redis& redis) {std::cout << "lpop 和 rpop" << std::endl;redis.flushall();// 构造一个 listredis.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;}
}void test4(Redis& redis) {using namespace std::chrono_literals;std::cout << "blpop" << std::endl;redis.flushall();auto result = redis.blpop({"key", "key2", "key3"}, 10s);if (result) {std::cout << "key:" << result->first << std::endl;std::cout << "elem:" << result->second << std::endl;} else {std::cout << "result 无效!" << std::endl;}
}void test5(Redis& redis) {std::cout << "llen" << std::endl;redis.flushall();redis.lpush("key", {"111", "222", "333", "444"});long long len = redis.llen("key");std::cout << "len: " << len << std::endl;
}int main() {Redis redis("tcp://127.0.0.1:6379");// test1(redis);// test2(redis);// test3(redis);// test4(redis);test5(redis);return 0;
}
lpush 和 lrange:test1()结果:
rpush:test2()结果:
lpop 和 rpo:test3()结果:
blpop:test4()结果:
llen:test5()结果:
6.4 set的使用
set.cpp 文件:
#include <iostream>
#include <vector>
#include <string>
#include <set>
#include <unordered_map>
#include <sw/redis++/redis++.h>#include "util.hpp"using std::cout;
using std::endl;
using std::vector;
using std::string;
using std::unordered_map;
using std::set;using sw::redis::Redis;void test1(Redis& redis) {std::cout << "sadd 和 smembers" << std::endl;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());// 获取到上述元素// 此处用来保存 smembers 的结果, 使用 set 可能更合适. vector<string> result;// auto it = std::back_inserter(result);// 由于此处 set 里的元素顺序是固定的. 指定一个 result.end() 或者 result.begin() 或者其他位置的迭代器, 都无所谓~~auto it = std::inserter(result, result.end());redis.smembers("key", it);printContainer(result);
}void test2(Redis& redis) {std::cout << "sismember" << std::endl;redis.flushall();redis.sadd("key", {"111", "222", "333", "444"});bool result = redis.sismember("key", "555");std::cout << "result: " << result << std::endl;
}void test3(Redis& redis) {std::cout << "scard" << std::endl;redis.flushall();redis.sadd("key", {"111", "222", "333"});long long result = redis.scard("key");std::cout << "result: " << result << std::endl;
}void test4(Redis& redis) {std::cout << "spop" << std::endl;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;}
}void test5(Redis& redis) {std::cout << "sinter" << std::endl;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);
}void test6(Redis& redis) {std::cout << "sinterstore" << std::endl;redis.flushall();redis.sadd("key1", {"111", "222", "333"});redis.sadd("key2", {"111", "222", "444"});long long len = redis.sinterstore("key3", {"key1", "key2"});std::cout << "len: " << len << std::endl;set<string> result;auto it = std::inserter(result, result.end());redis.smembers("key3", it);printContainer(result);
}int main() {Redis redis("tcp://127.0.0.1:6379");// test1(redis);// test2(redis);// test3(redis);// test4(redis);// test5(redis);test6(redis);return 0;
}
sadd 和 smembers:test1()结果:
sismember:test2()结果:
scard:test3()结果:
spop:test4()结果:
sinter:test5()结果:
sinterstore:test6()结果:
6.5 hash的使用
hash.cpp 文件:
#include <iostream>
#include <vector>
#include <string>
#include <unordered_map>
#include <sw/redis++/redis++.h>#include "util.hpp"using std::cout;
using std::endl;
using std::vector;
using std::string;
using std::unordered_map;using sw::redis::Redis;void test1(Redis& redis) {std::cout << "hset 和 hget" << std::endl;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;}
}void test2(Redis& redis) {std::cout << "hexits" << std::endl;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;
}void test3(Redis& redis) {std::cout << "hdel" << std::endl;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;
}void test4(Redis& redis) {std::cout << "hkeys 和 hvals" << std::endl;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);
}void test5(Redis& redis) {std::cout << "hmget 和 hmset" << std::endl;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);
}int main() {Redis redis("tcp://127.0.0.1:6379");// test1(redis);// test2(redis);// test3(redis);// test4(redis);test5(redis);return 0;
}
hset 和 hget:test1()结果:
hexits:test2()结果:
hdel:test3()结果:
hkeys 和 hvals:test4()结果:
hmget 和 hmset:test5()结果:
6.6 zset的使用
zset.cpp 文件:
#include <iostream>
#include <vector>
#include <string>
#include <unordered_map>
#include <sw/redis++/redis++.h>#include "util.hpp"using std::cout;
using std::endl;
using std::vector;
using std::string;
using std::unordered_map;using sw::redis::Redis;void test1(Redis& redis) {std::cout << "zadd 和 zrange" << std::endl;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());// zrange 支持两种主要的风格:// 1. 只查询 member, 不带 score// 2. 查询 member 同时带 score// 关键就是看插入迭代器指向的容器的类型. // 指向的容器只是包含一个 string, 就是只查询 member// 指向的容器包含的是一个 pair, 里面有 string 和 double, 就是查询 member 同时带有 scorevector<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);
}void test2(Redis& redis) {std::cout << "zcard" << std::endl;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;
}void test3(Redis& redis) {std::cout << "zrem" << std::endl;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;
}void test4(Redis& redis) {std::cout << "zscore" << std::endl;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;}
}void test5(Redis& redis) {std::cout << "zrank" << std::endl;redis.flushall();redis.zadd("key", "zhangsan", 90);redis.zadd("key", "lisi", 91);redis.zadd("key", "wangwu", 92);redis.zadd("key", "zhaoliu", 93);auto rank = redis.zrank("key", "zhaoliu");if (rank) {std::cout << "rank: " << rank.value() << std::endl;} else {std::cout << "rank 无效" << std::endl;}
}int main() {Redis redis("tcp://127.0.0.1:6379");// test1(redis);// test2(redis);// test3(redis);// test4(redis);test5(redis);return 0;
}
zadd 和 zrange:test1()结果:
zcard:test2()结果:
zrem:test3()结果:
zscore:test4()结果:
zrank:test5()结果:
redis学习打卡🥳
相关文章:

【redis初阶】redis客户端
目录 一、基本介绍 二、认识RESP(redis自定的应用层协议名称) 三、访问github的技巧 四、安装redisplusplus 4.1 安装 hiredis** 4.2 下载 redis-plus-plus 源码 4.3 编译/安装 redis-plus-plus 五、编写运行helloworld 六、redis命令演示 6.1 通用命令的…...

【ComfyUI专栏】ComfyUI 部署Kolors
什么是Kolors?我相信一定会有朋友可能第一次听说这个生图的模型,开始我也很难想象,这竟然是快手推出的可灵AI的项目,我们可以直接利用模型来生成图片和视频。 大家可以通过直接访问可灵AI的网址获取到可灵的项目,但是对于我们来说我们需要基于ComfyUI来生成必要的图片和视…...

深入了解 HTTP 头部中的 Accept-Encoding:gzip、deflate、br、zstd
在现代Web开发中,性能优化是至关重要的一部分。HTTP协议中的Accept-Encoding头部正是为性能提升提供了一个非常有效的方式,它告知服务器客户端能够理解并接收哪些压缩算法的响应内容。在这篇博客中,我们将详细探讨Accept-Encoding头部的作用&…...

【含代码】逆向获取 webpack chunk 下的__webpack_require__ 函数,获悉所有的模块以及模块下的函数
背景 Webpack 打包后的代码是不会直接暴露 __webpack_require__ 函数,目的是为了避免污染全局变量同时也为了保护 webpack 的打包后的模块都隐藏在闭包函数里,达到数据的安全性。 而有时我们为了测试某个函数,想直接获取这个内置函数&#…...

2025牛客寒假算法基础集训营2
H 一起画很大的圆! 看起来像是一道计算几何的题,实际上通过分析和猜想,是有O1复杂度的结论的。具体证明略,结论是三点越接近共线,得出的半径越大。 #include <bits/stdc.h> using namespace std; #define endl \…...

落地 ORB角点检测与sift检测
ORB角点检测 可以说ORB是由FAST、灰度质心和BRIEF等技术组合优化形成的,不过更准确地说,ORB是在FAST特征检测算法基础上,结合了灰度质心确定方向以及改进后的BRIEF描述子等技术形成的,以下是具体分析: • FAST特征检…...

16 分布式session和无状态的会话
在我们传统的应用中session存储在服务端,减少服务端的查询压力。如果以集群的方式部署,用户登录的session存储在该次登录的服务器节点上,如果下次访问服务端的请求落到其他节点上就需要重新生成session,这样用户需要频繁的登录。 …...

SpringBoot整合Swagger UI 用于提供接口可视化界面
目录 一、引入相关依赖 二、添加配置文件 三、测试 四、Swagger 相关注解 一、引入相关依赖 图像化依赖 Swagger UI 用于提供可视化界面: <dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger-ui</artifactI…...

如何实现滑动开关功能
文章目录 1 概念介绍2 使用方法3 示例代码 我们在上一章回中介绍了PageView这个Widget,本章回中将介绍Switch Widget.闲话休提,让我们一起Talk Flutter吧。 1 概念介绍 我们在这里介绍的Switch是指左右滑动的开关,常用来表示某项设置是打开还是关闭。Fl…...

数仓的数据加工过程-ETL
ETL代表Extract Transform和Load。ETL将所有三个数据库功能组合到一个工具中,以从一个数据库获取数据并将其放入另一个数据库。 提取:提取是从数据库中提取(读取)信息的过程。在此阶段,从多个或不同类型的来源收集数据。 转换:转…...

自动驾驶中的多传感器时间同步
目录 前言 1.多传感器时间特点 2.统一时钟源 2.1 时钟源 2.2 PPSGPRMC 2.3 PTP 2.4 全域架构时间同步方案 3.时间戳误差 3.1 硬件同步 3.2 软件同步 3.2.3 其他方式 ① ROS 中的 message_filters 包 ② 双端队列 std::deque 参考: 前言 对多传感器数据…...

CYT3BB_4BB:Clock system
CYT3BB/4BB的时钟系统包括8-MHz IMO、2个ILO、4个看门狗计时器、4个PLL、一个FLL、5个时钟监控器(CSV)、一个8-33.34MHzECO和一个32.768-kHz WCO。 该时钟系统支持三个主时钟域: CLK_HF、CLK_SLOW和CLK_LF。 - CLK_HFx: CLK_HFx是活动模式的时钟。每个人都可以使用任…...

C# OpenCV机器视觉:利用CNN实现快速模板匹配
在一个阳光灿烂的周末,阿强正瘫在沙发上,百无聊赖地换着电视频道。突然,一则新闻吸引了他的注意:某博物馆里一幅珍贵的古画离奇失踪,警方怀疑是被一伙狡猾的盗贼偷走了,现场只留下一些模糊不清的监控画面&a…...

消息队列篇--通信协议扩展篇--二进制编码(ASCII,UTF-8,UTF-16,Unicode等)
1、ASCII(American Standard Code for Information Interchange) 范围:0 到 127(共 128 个字符)描述:ASCII 是一种早期的字符编码标准,主要用于表示英文字母、数字和一些常见的符号。每个字符占…...

Direct Preference Optimization (DPO): 一种无需强化学习的语言模型偏好优化方法
论文地址:https://arxiv.org/pdf/2305.18290 1. 背景与挑战 近年来,大规模无监督语言模型(LM)在知识获取和推理能力方面取得了显著进展,但如何精确控制其行为仍是一个难题。 现有的方法通常通过**强化学习从人类反馈&…...

跟我学C++中级篇——容器的连接
一、数据的整合 在实际的开发场景中,经常可以遇到以下的情况:有几个地方的数据需要整合在一起。解决办法也有很多,在不同的层面有不同的解决方式。比如经过清洗可以把非关系型数据转为关系型数据。但在底层编程的情况中会发现有几情况&#…...

java求职学习day15
多线程 1 基本概念 1.1 程序和进程的概念 (1)程序 - 数据结构 算法,主要指存放在硬盘上的可执行文件。 (2)进程 - 主要指运行在内存中的可执行文件。 (3)目前主流的操作系统都支持多进程&a…...

【脚本】如何禁用谷歌浏览器自动更新
这里写自定义目录标题 问题描述解决方法代码 问题描述 最近更新系统以后,发现chrome老是自己更新,导致我的代码也得跟着他更新,就跟一个拜托不掉的狗皮膏药一样。 解决方法 于是我写了一个脚本,以下代码都是bash代码࿰…...

【Linux】华为服务器使用U盘安装统信操作系统
目录 一、准备工作 1.1 下载UOS官方系统 1.2制作启动U盘 1.3 服务器智能管理系统iBMC 二、iBMC设置U盘启动 一、准备工作 1.1 下载UOS官方系统 服务器CPU的架构是x86-64还是aarch64),地址:统信UOS生态社区 - 打造操作系统创…...

WPF3-在xaml中引用其他程序集的名称空间
1. 如何在XAML里引用类库中的名称空间和类2. 小结 1. 如何在XAML里引用类库中的名称空间和类 首先需要记住一点:把类库引用到项目中是引用其中名称空间的物理基础,无论是C#还是XAML都是这样。 一旦将一个类库引用进程序,就可以引用其中的名…...

Python 在Word中添加、或删除超链接
在Word文档中,超链接是一种将文本或图像连接到其他文档、网页或同一文档中不同部分的功能。通过添加超链接,用户可以轻松地导航到相关信息,从而增强文档的互动性和可读性。本文将介绍如何使用Python在Word中添加超链接、或删除Word文档中的超…...

基于 WPF 平台使用纯 C# 实现动态处理 json 字符串
一、引言 在当今的软件开发领域,数据的交换与存储变得愈发频繁,JSON(JavaScript Object Notation)作为一种轻量级的数据交换格式,以其简洁、易读、便于解析和生成的特点,被广泛应用于各种应用程序中。在 W…...

「全网最细 + 实战源码案例」设计模式——单例设计模式
核心思想: 属于创建型设计模式,核心目的是确保一个类在整个程序运行期间只有一个实例,并提供一个全局访问点来获取该实例。 控制共享资源的访问(如数据库链接、配置管理、日志处理器等) 真实世界类比:政府…...

第01章 19 通过点数据逐级构建球体体数据的综合性小例子
用VTK库来创建一个三维图像数据(vtkImageData),并填充标量数据以表示一个球体的体数据。球的半径为50,体数据的空间间隔为1.0/1000。 首先,我需要包含VTK的头文件,并且创建一个vtkImageData对象。然后&…...

CVE-2024-23897-Jenkins任意文件读取漏洞复现
content Jenkins是什么CVE-2024-23897总结修复建议 Jenkins是什么 Jenkins是一人基于Java开发的、可扩展的持续集成引擎,用于持续、自动地构建/测试软件项目,可以监控一些定时执行的任务。 官网文档: Jenkins是一款开源 CI&CD 软件&…...

前端react后端java实现提交antd form表单成功即导出压缩包
前端(React Ant Design) 1. 创建表单:使用<Form>组件来创建你的表单。 2. 处理表单提交:在onFinish回调中发起请求到后端API,并处理响应。 import React from react; import { Form, Input, Button } from ant…...

基于ESP32的桌面小屏幕实战[6]:环境搭建和软件基础
摘要 本文分为两部分:Linux开发环境搭建和软件基础。Linux开发环境搭建介绍了Ubuntu虚拟机安装及SSH、Samba配置,可以实现用VSCode操作虚拟机。为了后续工作,搭建了乐鑫ESP32 SDK环境。软件基础介绍了Linux开发常用的软件基础,包…...

接口(完)
大家好,今天我们着重来总结一下接口的知识,并且将接口和抽象类的区别罗列一下,帮助我们更好的认识抽象类和接口。 2.7 抽象类和接口的区别. 抽类和接口都是Java中多态的常见使用方式,都需要重点掌握,同时又要认清两者的区别(重要!!…...

数据结构——实验七·排序
欢迎各位大佬们来到Tubishu的博客🌟 Tubishu是一名计算机本科生,不定期发送一些在学校的成果供佬们消遣~希望能为佬的编程之路添砖加瓦⭐🔥 求各位大佬们垂怜🔥点赞评论一下呗🔥🔥 本文专栏 ➡️ 数据结构 …...

JVM堆空间
JVM(Java虚拟机)堆空间是Java内存管理的核心区域之一,用于存储Java对象实例。以下是关于JVM堆空间的详细介绍: 1. 堆空间的作用 • 存储对象实例:几乎所有的Java对象实例(通过new关键字创建的对象…...