C++ 操作 Redis 客户端

引言

前面几篇文章都在介绍 Redis 原生命令行客户端,在实际应用开发中,开发人员更希望使用针对特定编程语言的专用客户端,通过编程的方式操作 Redis 数据库。因此,Redis 支持多种编程语言。本文将介绍 如何使用 C++ 语言来操作 Redis

一、认识 RESP 协议

RESP(Redis serialization protocol)是 Redis 自定义的应用层协议,所有编程语言的 Redis 库都是基于这个协议开发的。它设计的核心目标是简单易实现、解析高效,同时兼顾人类可读性,使得开发者能轻松调试 Redis 通信过程。这里仅做简单介绍,详细内容可参考Redis 官方文档 :Redis 的 RESP 协议

1. RESP 协议的核心特点

  1. 文本协议:基于 ASCII 字符编码,协议内容可直接通过肉眼识别(区别于二进制协议),便于调试。
  2. 类型明确:通过特定前缀字符标识数据类型(如字符串、整数、数组等),解析时无需额外判断。
  3. 高效解析:协议格式规则简单,解析器可通过线性扫描快速处理,几乎无性能开销。
  4. 扩展性强:支持复杂数据结构(如嵌套数组),能满足 Redis 多样化命令和返回值的需求。
  5. 一问一答式:客户端一次请求,服务器就会回答一次

2. RESP 协议的基本数据类型及格式

RESP 通过首字符区分数据类型,常见类型及格式如下:

类型前缀字符格式说明示例
简单字符串++字符串内容\r\n(不含换行符,用于返回简单结果,如 “OK”)+OK\r\n
错误消息--错误类型: 错误描述\r\n(用于返回错误信息,如命令不存在)-ERR unknown command 'foo'\r\n
整数::整数数值\r\n(用于返回计数、状态码等,如 EXISTS 命令的返回值):1\r\n(表示存在该键)
批量字符串$$长度\r\n内容\r\n(用于存储二进制安全的字符串,长度为 -1 表示空值)$5\r\nhello\r\n(表示字符串 “hello”)
数组**元素数量\r\n元素1\r\n元素2\r\n...(可嵌套数组,用于表示命令或复杂返回值)*3\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$7\r\nmyvalue\r\n(表示 SET mykey myvalue 命令)

3.、Redis 命令的传输流程(基于 RESP)

Redis 客户端与服务器的通信均通过 RESP 协议完成,以 SET mykey "hello" 命令为例,流程如下:

  1. 客户端发送命令
    命令以数组类型传输,数组元素为命令名和参数(均为批量字符串):

    *3\r\n          // 数组包含3个元素(SET、mykey、hello)  
    $3\r\nSET\r\n   // 第一个元素:命令名 "SET"(长度3)  
    $5\r\nmykey\r\n // 第二个元素:键 "mykey"(长度5)  
    $5\r\nhello\r\n // 第三个元素:值 "hello"(长度5)  
    
  2. 服务器返回结果
    若命令执行成功,返回简单字符串 +OK\r\n;若失败,返回错误消息(如 -ERR syntax error\r\n)。

4. RESP 协议的优势与适用场景

  • 优势

    • 实现简单:开发者可快速编写解析器(无需处理复杂的二进制格式)。
    • 调试友好:通过 telnetnc 等工具可直接发送 RESP 格式命令与 Redis 交互(如 telnet localhost 6379 后输入 *2\r\n$4\r\nPING\r\n$0\r\n\r\n 测试连接)。
    • 兼容性强:支持所有 Redis 命令及返回值类型,包括复杂结构(如 HGETALL 返回的键值对数组)。
  • 适用场景

    • 所有 Redis 客户端与服务器的通信(如 C++ 的 redisplusplus、Python 的 redis-py、Java 的 Jedis 等均基于 RESP 实现)。
    • 自定义 Redis 客户端开发(需严格遵循 RESP 格式组装命令和解析响应)。

我们实际使用时,不需要按照协议自动组织,只需要使用Redis 官方提供或者大佬们开源的库就可以,我们接下来将使用 redis-plus-plus 库,它是 C++ 语言的 Redis库

二、ubuntu 安装 redis-plus-plus

1. 安装 hiredis

hiredis 是 Redis 官方为 c 语言提供的客户端支持,是redis-plus-plus 必备的依赖库
通过包管理器来安装:

sudo apt install libhiredis-dev

2.安装 redis-plus-plus

只能通过源码编译的方式安装

git clone https://github.com/sewenew/redis-plus-plus.gitcd redis-plus-plusmkdir buildcd buildcmake ..makesudo make installcd ..

依次执行完命令就安装好了,然后创建的这些目录我们都可以直接删除了

三、c++ 操作 Redis

环境安装好了,我们就可以通过编写 C++ 代码来操作 Redis 了

1. c++ redis的第一个程序

hello.cc

#include <sw/redis++/redis++.h>
#include <sw/redis++/redis.h>#include <iostream>
#include <string>
#include <unordered_map>
#include <vector>using std::cout;
using std::endl;
using std::string;
using std::unordered_map;
using std::vector;int main()
{// 创建 Redis 对象,需指定 Redis服务器ip地址和端口sw::redis::Redis redis("tcp://127.0.0.1:6379");// 调用 ping 方法,让客户端发送一个 ping 命令,服务端会返回一个 pongstd::string result = redis.ping();std::cout << result << std::endl;return 0;
}

这段代码就是创建一个 Redis 对象,然后向 Redis 服务器发送一个 ping 命令,于是 Redis 服务器就会返回一个 pong 。

Makefile编写
我们需要注意 makefile 编写中需要指定库的位置,否则无法编译

hello:hello.ccg++ -std=c++17 -o $@ $^ /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -pthreadPHONY:clean
clean:rm -rf hello

编译运行:

至此,使用 c++ 操作 Redis 的第一个程序就完成了!

2. 使用 c++ 执行 Redis 通用命令

代码框架


#include <sw/redis++/redis++.h>
#include <sw/redis++/redis.h>#include <iostream>
#include <string>void test()
{// 每个命令写在这里// ...
}int main()
{sw::redis::Redis redis("tcp://127.0.0.1:6379");return 0;
}

首先给出代码框架,后续测试命令都会新创建一个 test 函数,然后再主函数中调用
Makefile:

.PHONY:all
all: hello generic	
hello:hello.ccg++ -std=c++17 -o $@ $^ /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -pthread
generic:generic.ccg++ -std=c++17 -o $@ $^ /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -pthreadPHONY:clean
clean:rm -rf hello generic

get 和 set

set接口

可以看到这些参数和set命令的选项有很大关联:

  • StringView 类型是 redis-plus-plus 提供的只读字符串,不能修改,这是由于只读字符产效率更高
  • 参数 key参数 val: 都是 StringView 类型
  • 参数 ttl:以毫秒为单位的过期时间
  • updatetype : 相当于 NX 和 XX

get接口

  • get的返回值是 OptionalString,这个类型会对 空值 (nil)做特殊处理,c++ 14后也引入了 std::optional_string,具体用法见示例代码。
// get 和 set
void test1(sw::redis::Redis &redis)
{std::cout << "get 和 set 的使用" << std::endl;// 清空一下数据库,避免之前数据的干扰,生产环境禁用!redis.flushall();// 使用 set 设置几个 keyredis.set("key1", "111");// 使用 get 获取到 key 对应的 valueauto value1 = redis.get("key1");if (value1){std::cout << "value1=" << value1.value() << std::endl;}// key2 是空值,这里如果不加判断会抛出异常auto value2 = redis.get("key2");if (value2){std::cout << "value2=" << value2.value() << std::endl;}
}

运行结果(不要忘记在主函数中调用exists):
value2是空值,由于if语句,不会输出

exists


返回类型是 long long,支持查询多个key,返回存在的个数
generic.cc

// exists 命令
void test2(sw::redis::Redis &redis)
{std::cout << "exists 的 使用" << std::endl;// 清空一下数据库,避免之前数据的干扰,生产环境禁用!redis.flushall();redis.set("key1", "value1");redis.set("key2", "value2");auto ret = redis.exists("key1");std::cout << ret << std::endl;ret = redis.exists("key3");std::cout << ret << std::endl;ret = redis.exists({"key1", "key2", "key3"});std::cout << ret << std::endl;
}

运行结果(记得在主函数中调用):

del

返回删除成功的个数

// del 命令
void test3(sw::redis::Redis &redis)
{std::cout << "del 的使用" << std::endl;// 清空一下数据库,避免之前数据的干扰,生产环境禁用!redis.flushall();redis.set("key1", "value1");redis.set("key2", "value2");auto ret = redis.del({"key1", "key2", "key3"});std::cout << ret << std::endl;ret = redis.exists({"key1", "key2", "key3"});std::cout << ret << std::endl;
}

运行结果:

keys

  • 参数 pattern : 匹配规则
  • 参数 output: 插入迭代器,我们可以事先构建一个容器,传入迭代器,这样 keys就会把查询到的元素插入容器中
// keys 命令
void test4(sw::redis::Redis &redis)
{std::cout << "keys 的使用" << std::endl;// 清空一下数据库,避免之前数据的干扰,生产环境禁用!redis.flushall();redis.set("key1", "value1");redis.set("key2", "value2");redis.set("key3", "value2");redis.set("key4", "value2");redis.set("key5", "value2");// redis 的keys 有两个参数,第一个是匹配规则// 第二个是 “插入迭代器”,我们可以事先构建一个容器,传入迭代器// 这样 keys就会把查询到的元素插入容器中std::vector<std::string> result;auto it = std::back_inserter(result);redis.keys("*", it);// 打印容器相关内容for (auto &str : result){std::cout << str << std::endl;}
}

expire 和 ttl

// expire and ttl
void test5(sw::redis::Redis &redis)
{std::cout << "expire and ttl" << std::endl;// 清空一下数据库,避免之前数据的干扰,生产环境禁用!redis.flushall();redis.set("key1", "111");redis.expire("key1", std::chrono::seconds(10));std::this_thread::sleep_for(std::chrono::seconds(5));long long ret = redis.ttl("key1");std::cout << "剩余时间: " << ret << std::endl;
}

type

返回值是string类型

// type
void test6(sw::redis::Redis &redis)
{std::cout << "type" << std::endl;// 清空一下数据库,避免之前数据的干扰,生产环境禁用!redis.flushall();redis.set("key1", "111");redis.lpush("key2", "111");std::cout << redis.type("key1") << std::endl;std::cout << redis.type("key2") << std::endl;
}

执行结果:

3. String

代码框架

#include <sw/redis++/cxx_utils.h>
#include <sw/redis++/redis++.h>
#include <sw/redis++/redis.h>#include <iostream>
#include <iterator>
#include <vector>// mget 和 mset
void test1(sw::redis::Redis& redis)
{
}int main()
{sw::redis::Redis redis("tcp://127.0.0.1:6379");// test1(redis);// test2(redis);test3(redis);return 0;
}

Makefile:

.PHONY:all
all: hello generic stringhello:hello.ccg++ -std=c++17 -o $@ $^ /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -pthreadgeneric:generic.ccg++ -std=c++17 -o $@ $^ /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -pthreadstring:string.ccg++ -std=c++17 -o $@ $^ /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -pthreadPHONY:clean
clean:rm -rf hello generic string

mset 和 mget

支持一次性设置多个值和获取多个值

// mget 和 mset
void test1(sw::redis::Redis& redis)
{std::cout << "mget 和 mset" << std::endl;// 清空一下数据库,避免之前数据的干扰,生产环境禁用!redis.flushall();// 第一种方法,直接使用初始化列表// redis.mset({std::make_pair("key1", "1"), std::make_pair("key2", "2"),//             std::make_pair("key3", "3")});// 第二种方法,通过迭代器构造std::vector<std::pair<std::string, std::string>> keys = {{"key1", "111"}, {"key2", "222"}, {"key3", "333"}};redis.mset(keys.begin(), keys.end());std::vector<sw::redis::OptionalString> results;auto it = std::back_inserter(results);redis.mget({"key1", "key2", "key3"}, it);for (auto& str : results){if (str){std::cout << str.value() << std::endl;}}
}
  • mset 支持两种方式设置:
    • 一是直接通过初始化列表传入pair
    • 二是构造一个容器,传入迭代器
  • mget 返回值是 OptionalString 也是通过插入迭代器的方式进行实现

getrange 和 setrange

void test2(sw::redis::Redis& redis)
{std::cout << "mget 和 mset" << std::endl;// 清空一下数据库,避免之前数据的干扰,生产环境禁用!redis.flushall();redis.set("key", "abcdefghjk");std::string result = redis.getrange("key", 2, 5);std::cout << "result: " << result << std::endl;redis.setrange("key", 2, "xyz");result = redis.getrange("key", 0, -1);std::cout << "result: " << result << std::endl;
}

运行结果:

incr 和 decr

// incr decr
void test3(sw::redis::Redis& redis)
{std::cout << "incr 和 decr" << std::endl;// 清空一下数据库,避免之前数据的干扰,生产环境禁用!redis.flushall();redis.set("key1", "100");redis.set("key2", "100");redis.incr("key1");redis.decr("key2");std::cout << redis.get("key1").value() << std::endl;std::cout << redis.get("key2").value() << std::endl;
}

运行结果:

4. List

代码框架

#include <sw/redis++/redis++.h>
#include <sw/redis++/redis.h>#include <iostream>
#include <iterator>int main()
{sw::redis::Redis redis("tcp://127.0.0.1:6379");// test1(redis);// test2(redis);test3(redis);return 0;
}

Makefile:

.PHONY:all
all: hello generic string listhello:hello.ccg++ -std=c++17 -o $@ $^ /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -pthreadgeneric:generic.ccg++ -std=c++17 -o $@ $^ /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -pthreadstring:string.ccg++ -std=c++17 -o $@ $^ /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -pthreadlist:list.ccg++ -std=c++17 -o $@ $^ /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -pthreadPHONY:clean
clean:rm -rf hello generic string list

lpush 和 lrange

// lpush 和 lrange
void test1(sw::redis::Redis& redis)
{std::cout << "lpush and lrange" << std::endl;// 清空一下数据库,避免之前数据的干扰,生产环境禁用!redis.flushall();// 插入单个元素redis.lpush("key", "1");// 插入一组元素,基于初始化列表redis.lpush("key", {"2", "3", "4"});// 插入一组元素,基于迭代器std::vector<std::string> values = {"5", "6", "7"};redis.lpush("key", values.begin(), values.end());// lrange 获取结果,需要构造容器,传入插入迭代器std::vector<std::string> results;auto it = std::back_inserter(results);redis.lrange("key", 0, -1, it);// 打印结果for (auto& result : results){std::cout << result << std::endl;}
}

lpop 和 rpop

// lpop 和 rpop
void test2(sw::redis::Redis& redis)
{std::cout << "lpop and rpop" << std::endl;// 清空一下数据库,避免之前数据的干扰,生产环境禁用!redis.flushall();redis.rpush("key", {"1", "2", "3", "4", "5", "6", "7"});auto ret = redis.lpop("key");if (ret){std::cout << "lpop: " << ret.value() << std::endl;}ret = redis.rpop("key");if (ret){std::cout << "rpop: " << ret.value() << std::endl;}
}

// blpop
void test3(sw::redis::Redis& redis)
{std::cout << "blpop" << std::endl;// 清空一下数据库,避免之前数据的干扰,生产环境禁用!redis.flushall();auto ret = redis.blpop({"key1", "key2", "key3"});if (ret){std::cout << "key: " << ret.value().first << std::endl;std::cout << "elem: " << ret.value().second << std::endl;}else{std::cout << "元素无效!" << std::endl;}
}


一开始会阻塞住,直到我们往 key1 key2 key3之一插入一个值

5. Set

代码框架

.PHONY:all
all: hello generic string list sethello:hello.ccg++ -std=c++17 -o $@ $^ /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -pthreadgeneric:generic.ccg++ -std=c++17 -o $@ $^ /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -pthreadstring:string.ccg++ -std=c++17 -o $@ $^ /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -pthreadlist:list.ccg++ -std=c++17 -o $@ $^ /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -pthreadset:set.ccg++ -std=c++17 -o $@ $^ /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -pthreadPHONY:clean
clean:rm -rf hello generic string list set

sadd 和 smembers

// sadd smembers
void test1(sw::redis::Redis &redis)
{std::cout << "sadd and smembers" << std::endl;// 清空一下数据库,避免之前数据的干扰,生产环境禁用!redis.flushall();// 一次添加一个元素redis.sadd("key", "111");// 一次添加多个元素(使用初始化列表)redis.sadd("key", {"222", "333"});// 一次添加多个元素(使用迭代器)std::vector<std::string> elems = {"444", "555"};redis.sadd("key", elems.begin(), elems.end());// 通过 smembers 获取返回结果//  1. vector//  2. set// std::vector<std::string> results;// auto it = std::back_inserter(results);std::set<std::string> results;auto it = std::inserter(results, results.end());redis.smembers("key", it);// 打印结果for (auto &res : results){std::cout << res << std::endl;}
}

sismember 和 spop

// sismember and spop
void test2(sw::redis::Redis &redis)
{std::cout << "sismember and spop" << std::endl;// 清空一下数据库,避免之前数据的干扰,生产环境禁用!redis.flushall();redis.sadd("key", {"111", "222", "333"});bool result = redis.sismember("key", "111");std::cout << result << std::endl;redis.spop("key");redis.spop("key");redis.spop("key");result = redis.sismember("key", "111");std::cout << result << std::endl;
}

运行结果:

sinter 和 sinterstore

// sinter
void test3(sw::redis::Redis &redis)
{std::cout << "sinter" << std::endl;// 清空一下数据库,避免之前数据的干扰,生产环境禁用!redis.flushall();redis.sadd("key1", {"111", "222", "333"});redis.sadd("key2", {"111", "222", "444"});std::set<std::string> results;auto it = std::inserter(results, results.end());redis.sinter({"key1", "key2"}, it);for (auto &result : results){std::cout << result << std::endl;}
}// sinterstore
void test4(sw::redis::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;std::set<std::string> results;auto it = std::inserter(results, results.end());redis.smembers("key3", it);// 打印结果for (auto &res : results){std::cout << res << std::endl;}
}

6. Hash

代码框架

.PHONY:all
all: hello generic string list set hashhello:hello.ccg++ -std=c++17 -o $@ $^ /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -pthreadgeneric:generic.ccg++ -std=c++17 -o $@ $^ /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -pthreadstring:string.ccg++ -std=c++17 -o $@ $^ /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -pthreadlist:list.ccg++ -std=c++17 -o $@ $^ /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -pthreadset:set.ccg++ -std=c++17 -o $@ $^ /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -pthreadhash:hash.ccg++ -std=c++17 -o $@ $^ /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -pthreadPHONY:clean
clean:rm -rf hello generic string list set hash

hset 和 hget

// hget and hset
void test1(sw::redis::Redis &redis)
{std::cout << "hget and hset" << std::endl;// 清空一下数据库,避免之前数据的干扰,生产环境禁用!redis.flushall();// 插入一个 field-value 对redis.hset("key", "f1", "1");// 插入多个 field-value 对(初始化列表)redis.hset("key", {std::make_pair("f2", "2"), std::make_pair("f3", "3")});// 插入多个 field-value 对(迭代器)std::vector<std::pair<std::string, std::string>> fields = {std::make_pair("f4", "4"), std::make_pair("f5", "5")};auto result = redis.hget("key", "f1");if (result){std::cout << result.value() << std::endl;}else{std::cout << "返回值无效!" << std::endl;}
}

hexists,hdel 和 hlen

// hexists, hlen, hdel
void test2(sw::redis::Redis &redis)
{std::cout << "hget and hset" << std::endl;// 清空一下数据库,避免之前数据的干扰,生产环境禁用!redis.flushall();// 插入多个 field-value 对(初始化列表)redis.hset("key", {std::make_pair("f2", "2"), std::make_pair("f3", "3")});std::cout << "删除前:" << std::endl;long long len = redis.hlen("key");std::cout << "len: " << len << std::endl;bool result = redis.hexists("key", "f2");std::cout << result << std::endl;redis.hdel("key", "f2");std::cout << "删除后:" << std::endl;len = redis.hlen("key");std::cout << "len: " << len << std::endl;result = redis.hexists("key", "f2");std::cout << result << std::endl;
}

hkeys 和 hvals

// hkeys
// hkeys hvals
void test3(sw::redis::Redis &redis)
{std::cout << "keys hvals" << std::endl;// 清空一下数据库,避免之前数据的干扰,生产环境禁用!redis.flushall();// 插入多个 field-value 对(初始化列表)redis.hset("key", {std::make_pair("f1", "1"), std::make_pair("f2", "2"),std::make_pair("f3", "3")});std::vector<std::string> fields;auto it = std::back_inserter(fields);redis.hkeys("key", it);for (auto &str : fields){std::cout << str << std::endl;}std::vector<std::string> vals;it = std::back_inserter(vals);redis.hvals("key", it);for (auto &str : vals){std::cout << str << std::endl;}
}

7. Zset

.PHONY:all
all: hello generic string list set hash zsethello:hello.ccg++ -std=c++17 -o $@ $^ /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -pthreadgeneric:generic.ccg++ -std=c++17 -o $@ $^ /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -pthreadstring:string.ccg++ -std=c++17 -o $@ $^ /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -pthreadlist:list.ccg++ -std=c++17 -o $@ $^ /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -pthreadset:set.ccg++ -std=c++17 -o $@ $^ /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -pthreadhash:hash.ccg++ -std=c++17 -o $@ $^ /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -pthreadzset:zset.ccg++ -std=c++17 -o $@ $^ /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -pthreadPHONY:clean
clean:rm -rf hello generic string list set hash zset

zadd 和 zrange

zrange 查询是否带分数取决于插入迭代器对应容器的类型:

  • 如果插入迭代器对应一个仅包含 string 的类型的容器
  • 如果插入迭代器对应容器是 pair<string, double> 为元素的容器,则返回分数
// zadd zrange
void test1(sw::redis::Redis& redis)
{std::cout << "zadd zrange" << std::endl;// 清空数据库,防止之前的数据干扰,生产环境禁用!redis.flushall();// 三种版本redis.zadd("key", "吕布", 99);redis.zadd("key", {std::make_pair("赵云", 98), std::make_pair("张飞", 95)});std::vector<std::pair<std::string, double>> members = {std::make_pair("典韦", 97), std::make_pair("关羽", 96)};redis.zadd("key", members.begin(), members.end());// zrange 是否查询分数要看容器类型//  如果容器仅 string 则 仅查询member//  如果容器是 pair<string, double>, 则返回member 和 分数std::cout << "仅查询member" << std::endl;std::vector<std::string> results;auto it = std::back_inserter(results);redis.zrange("key", 0, -1, it);for (auto& s : results){std::cout << s << std::endl;}std::cout << "查询member和分数" << std::endl;std::vector<std::pair<std::string, double>> scores;auto it1 = std::back_inserter(scores);redis.zrange("key", 0, -1, it1);for (auto& [member, score] : scores){std::cout << member << " " << score << std::endl;}
}

zcard zrank zscore

// zcard zscore zrank
void test2(sw::redis::Redis& redis)
{std::cout << "zcard zscore 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);long long result = redis.zcard("key");std::cout << result << std::endl;auto score = redis.zscore("key", "zhangsan");if (score){std::cout << "score: " << score.value() << std::endl;}else{std::cout << "score 无效" << std::endl;}auto rank = redis.zrank("key", "lisi");if (score){std::cout << "rank: " << rank.value() << std::endl;}else{std::cout << "rank 无效" << std::endl;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/94521.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/94521.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

批量提问程序开发方案:基于Python的百度文小言接口实现

批量提问程序开发方案&#xff1a;基于Python的百度文小言接口实现 1. 项目概述 1.1 项目背景 在现代信息检索和自动化办公场景中&#xff0c;批量提问功能已成为提高工作效率的重要工具。本项目旨在开发一个基于Python的批量提问程序&#xff0c;专门针对百度文小言平台&am…

Apollo中三种相机外参的可视化分析

Apollo中三种相机外参的可视化分析一、什么是相机外参&#xff1f;为什么需要可视化&#xff1f;二、不同外参来源对比三、详细操作步骤1. 环境准备2. 获取 NuScenes外参数据3. 外参到空间位置的转换及可视化四、可视化对比1. NuScenes数据集外参2. Apollo BEV模型外参3. Apoll…

虚拟化KVM常用命令汇总

KVM&#xff08;Kernel-based Virtual Machine&#xff09;是一种开源的硬件虚拟化解决方案&#xff0c;它是 Linux 内核的一部分&#xff0c;允许在支持虚拟化技术的硬件&#xff08;如 Intel VT-x 或 AMD-V&#xff09;上运行虚拟机。KVM 将 Linux 内核转变为一个裸机虚拟机监…

6s081环境配置以及使用vscode连接本地wsl2

6s081环境配置以及使用vscode连接wsl2 本人环境&#xff1a;windows11、wsl2ubuntu20.04 课程&#xff1a;6s081的2020版本的:https://pdos.csail.mit.edu/6.S081/2020/schedule.html 一、wsl2ubuntu20.04配置6s081环境 注&#xff1a;关于如何在window中安装wsl&#xff0c;这…

C++实现线程池(3)缓存线程池

三. CachedThreadPool 的实现3.1 需求:动态调整线程数量&#xff1a;与 FixedThreadPool 不同&#xff0c;CachedThreadPool 的线程数量是动态调整的。当有新任务提交时&#xff0c;如果线程池中有空闲的线程&#xff0c;则会立即使用空闲线程执行任务&#xff1b;如果线程池中…

WMS+自动化立库:无人仓的现在进行时

传统仓库正面临严峻挑战&#xff1a;效率瓶颈日益凸显&#xff0c;人力成本持续攀升&#xff0c;空间利用率逼近极限&#xff0c;而订单响应速度却难以满足市场需求。如何破局&#xff1f;WMS&#xff08;仓库管理系统&#xff09;与自动化立体库&#xff08;AS/RS&#xff09;…

多模态大模型研究每日简报【2025-08-05】

训练数据相关 EditGarment: An Instruction-Based Garment Editing Dataset Constructed with Automated MLLM Synthesis and Semantic-Aware Evaluation (https://arxiv.org/abs/2508.03497)&#xff1a;提出了一种自动化的流程&#xff0c;用于构建服装编辑数据集EditGarmen…

4、docker数据卷管理命令 | docker volume

1、命令总览命令作用出现频率备注★ docker volume create新建卷高-d 指定驱动&#xff0c;-o 指定驱动选项★ docker volume ls列出卷高--filter danglingtrue 查孤儿卷★ docker volume inspect查看卷详情高输出 JSON&#xff0c;可加 --format★ docker volume rm删除卷高只…

计数组合学7.14(对偶 RSK 算法)

7.14 对偶 RSK 算法 存在 RSK 算法的一种变体&#xff0c;其与乘积 ∏i,j(1xiyj)\prod_{i,j}(1 x_{i}y_{j})∏i,j​(1xi​yj​) 的关系类似于 RSK 算法本身与 ∏i,j(1−xiyj)−1\prod_{i,j}(1 - x_{i}y_{j})^{-1}∏i,j​(1−xi​yj​)−1 的关系。我们称此变体为对偶 RSK 算法…

C语言中的进程、线程与进程间通信详解

目录 引言 基本概念 1. 进程&#xff08;Process&#xff09; 2. 线程&#xff08;Thread&#xff09; 线程编程实战 1. 常见线程库 2. 合理设置线程数 3. pthread 创建线程 线程同步机制 1. 互斥锁 pthread_mutex_t 2. 条件变量 pthread_cond_t 3. 读写锁 pthread…

[假面骑士] 555浅谈

假面骑士555(faiz)是我最先接触的一部平成系列的假面骑士&#xff0c;同时也是我个人最喜欢的一部假面骑士。一、大纲简介震惊&#xff0c;人类最新的进化形态——奥菲一诺&#xff0c;横空出世&#xff01;日本的顶级财团&#xff0c;Smart Brain&#xff0c;的前任社长&#…

Vue Router 路由的创建和基本使用(超详细)

一、路由的基本概念 你是否好奇单页应用&#xff08;SPA&#xff09;是如何在不刷新页面的情况下实现页面切换的&#xff1f;这就离不开路由的功劳。 路由&#xff1a;本质是一组 key-value 的对应关系&#xff0c;在前端领域中&#xff0c;key 通常是路径&#xff0c;value …

深入理解设计模式:策略模式的艺术与实践

在软件开发中&#xff0c;我们经常会遇到需要根据不同情况选择不同算法或行为的场景。传统的做法可能是使用大量的条件语句&#xff08;if-else或switch-case&#xff09;&#xff0c;但随着需求的增加和变化&#xff0c;这种硬编码的方式会导致代码难以维护和扩展。策略模式&a…

概率/期望 DP llya and Escalator

题目链接&#xff1a;Problem - D - Codeforces 看了这篇文章来的&#xff1a;【算法学习笔记】概率与期望DP - RioTian - 博客园 这篇博客写得挺好的&#xff0c;讲了一些常见方法&#xff0c;概率 / 期望的题多练练就上手了。 题目大意&#xff1a; n 个人排队上电梯&…

大陆电子MBDS开发平台转到其他国产控制器平台产生的问题记录

u8_StComLowSpdGearSwt变量为例&#xff0c;之前用的时候只有输入&#xff0c;没什么实际意义&#xff0c;导致新环境下编译报错&#xff0c;缺少声明&#xff0c;解决办法&#xff1a;注释掉输入模块。今天解决的另一个比较大的问题&#xff0c;不同模型函数公用函数模块生成代…

机器学习模型调优实战指南

文章目录模型选择与调优&#xff1a;从理论到实战1. 引言2. 模型评估&#xff1a;为选择提供依据2.1 偏差-方差权衡2.2 数据集划分与分层抽样2.3 交叉验证&#xff08;Cross-Validation&#xff09;2.4 信息准则&#xff08;AIC / BIC&#xff09;3. 超参数调优&#xff1a;让模…

【教程】Unity CI/CD流程

测试机&#xff1a;红帽 Linux8 源码仓库&#xff1a;Gitee - MrRiver/Unity Example   系统环境准备 1&#xff09;yum 源 sudo curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-8.repo sudo sed -i s/\$releasever/8/g /etc/yum.repos…

文献阅读 | Briefings in Bioinformatics | Hiplot:全面且易于使用的生物医学可视化分析平台

文献介绍文献题目&#xff1a; Hiplot&#xff1a;一个综合且易于使用的 Web 服务&#xff0c;用于增强出版物准备的生物医学数据可视化 研究团队&#xff1a; Openbiox/Hiplot 社区 发表时间&#xff1a; 2022-07-05 发表期刊&#xff1a; Briefings in Bioinformatics 影响因…

【数字图像处理系列笔记】Ch04:灰度变换与空间域图像增强(2)

目录 一、空域滤波基础 一、空域滤波的基本概念 二、空域滤波的数学原理 三、空域滤波器的分类与典型示例 &#xff08;一&#xff09;线性滤波器&#xff08;Linear Filter&#xff09; &#xff08;二&#xff09;非线性滤波器&#xff08;Non-linear Filter&#xff0…

AI浪潮下,FPGA如何实现自我重塑与行业变革

引言&#xff1a;AI 与 FPGA&#xff0c;新时代的碰撞 2025 年&#xff0c;人工智能技术迎来爆发式增长&#xff0c;大模型、生成式 AI 和多模态技术持续突破&#xff0c;人形机器人量产元年正式开启&#xff0c;自动驾驶商业化进程加速&#xff0c;工业数字化转型全面铺开(1)…