目录
2.0 StringView,和OptionalString类型
一,环境配置
1.1 介绍
有很多操作Redis的第三方库,这里我们主要使用“redis-plus-plus”这个,因为这些库虽然很多,但大多数都是大同小异,网址为:sewenew/redis-plus-plus: Redis client written in C++ (github.com)
所以我们一共需要安装两个东西,我们可以直接用Linux的包管理器安装hiredis和redis-plus-plus,Ubuntu版本使用apt即可,Centos版本使用yum即可,关于这两个Linux前面也已经介绍过:Linux操作系统基础开发工具的使用——vim,gcc/g++,MakeFile,gdb,yum/apt_vim makefile-CSDN博客
1.2 安装hiredis
①OK我们先来安装hiredis:
Ubuntu:
sudo apt install libhiredis-dev
Centos:
sudo yum install hiredis-devel.x86_64
1.3 安装redis-plus-plus
②接下来我们安装redis-plus-plus的本体,这个安装比较麻烦,只能通过源码编译去安装了,这里只演示Ubuntu环境下的安装:
首先是下载源码:
sudo git clone https://github.com/sewenew/redis-plus-plus.git
关于cmake:
- redis-plus-plus是使用cmake作为构建工具的,cmake先当与是makefile的升级版
- makefile本身功能比较简陋,比较原始,写起来也比较麻烦,实际开发中很少会去手写makefile
- 所以我们一般通过程序来生成makefile,cmake就是一个生成makefile的工具
- cmake好比是C语言,makefile好比是汇编语言
sudo apt install cmake
下面是编译的具体步骤,下面的各种操作最好都加上sudo或者直接切换成root用户:
然后就可以直接使用make进行编译,需要的时间可能要一会儿,最后生成的动静态库如下:
后续写代码不一定能找到这里的库,所以推荐把这些库,拷贝到系统目录中,而且这步操作也不用我们自己搞,直接使用下面的命令即可:
make install
可以直接把内容拷贝到系统目录里了,包括链接等过程
很多C++中的库,都是需要编译安装的,而具体的操作大多类似
1.4 连接服务器
接下来就是使用redis-plus-plus连接服务器了,再使用ping命令,来检测一下连通性:
直接在自己认为的合适位置创建test目录,创建hello.cc源码,使用VSCode开始编写代码
首先要包含redis的入口头文件,一般放在下面目录中:
hello.cc代码如下:
#include <iostream> //尖括号是在系统目录中搜索头文件,引号是在项目目录中搜索头文件 #include <sw/redis++/redis++.h> //sw和Redis没有直接关系,sw是作者名字的缩写,日常写代码的时候,尽量不要用缩写,容易误会 #include <string> using std::cout; using std::endl; using sw::redis::Redis; // 使用Redis库提供的类,用这个类去创建对象 int main() { Redis redis("tcp://127.0.0.1:6379"); // 在构造函数中指定redis服务器的地址和端口,就是一个URL,唯一资源定位符 std::string result = redis.ping(); // 给Redis服务器发送ping请求,使用result接收,是字符串类型 cout << result << endl; return 0; }
注意:在使用makefile时需要引入库文件:
- redis++自己的静态库:
- hiredis的静态库:
- 线程库:就是-lpthread,直接一写即可
makefile如下:
hello:hello.cc g++ -o $@ $^ -std=c++17 /usr/local/lib/libredis++.a /usr/lib/x86_64-linux-gnu/libhiredis.a -pthread .PHONY:clean clean: rm hello
运行后输出PONG,这和Redis客户端的输出结果一样
表示连接成功,后续我们就是通过上面代码的Redis类里面的各种方法去操作Redis服务器的,而且那些方法和我们前面的Redis的命令是相配的
二,使用通用命令
Redis远程字典服务器(2) —— 全局命令-CSDN博客
2.0 StringView,和OptionalString类型
先看set函数的参数:
- 这里用到了一个类型叫做StringView,这个类型定义在 sw::redis 命名空间中,是“只读”的,不能修改,因为可以针对“只读”的优势做很多优化工作,效率比std::string更高
- 在C++17标准库中,也提供了一个std::string_view,因为redis-plus-plus是支持C++11,C++14和C++17的,所以如果你的服务器支持更高版本的C++标准库,那么就会去使用C++库里的std::string_view,如果你的服务器只有低版本的库,那么只会去使用 sw::redis 里面的
- StringView中的各种操作和string类似,只不过ban了修改操作,同时也支持了一些只读方法
再看一下get函数的返回值:
- 此处的OptionalString可以表示“非法值”或者“无效值” ,和命令行一样,如果key存在,就会返回value,如果key不存在,命令行就会返回一个“nil”,表示无效
- 而如果直接使用 std::string 来表示,不方便表示nil这个无效值;也可以使用指针返回nullptr来表示无效,但是返回指针又涉及到“内存归谁管”的类似问题
- boost库很早就引入了optional类型,在C++14中正式加入标准库,但是C++11没有,而redis-plus-plus又支持C++11,所以作者只能自己封装一个Optional,来适配服务器的C++标准库版本
2.1 set,get,
#include <iostream> #include <sw/redis++/redis++.h> using namespace std; using sw::redis::Redis; int main() { Redis redis("tcp://127.0.0.1:6379"); redis.flushall(); // 清空数据库,避免历史数据污染结果 redis.set("key1", "hello1"); // 返回值也和命令行一样,返回一个bool类型,后面的大部分命令的返回值都是和命令行一样的 redis.set("key2", "hello2"); redis.set("key3", "hello3"); auto value1 = redis.get("key1"); // 返回hello1 auto value4 = redis.get("key4"); // cout << "value1 = " << value1 << endl; //直接这样写会报错 // cout << "value4 = " << value4 << endl; //直接这样写会报错 // 次数value1和value4都是optional类型,但是sw::redis::Optional 不支持 << 运算符重载 // 此处我们也不需要给optional 再搞一个重载,可以把optional 当做只包含一个元素的容器,然后把这个元素取出来即可 cout << "value4 = " << value4.value() << endl; cout << "value1 = " << value1.value() << endl; return 0; }
编译能通过了,但是执行又报错了:
这是因为value4 是optional 的非法状态,那么optional此时就无法进行取值,会抛异常,导致程序崩溃,最后打印的结果是 Aborted,就是出发了Linux的SIGABRT信号,也就是6号信号
如何解决?可以使用try catch来捕获,但是实际上C++中不太经常用try catch,因为:
- 可能会消耗额外的运行时开销,对于追究极致性能的C++程序来说不太合适
- C++的try catch 相比于其他语言来说,太弱
可以使用if判断来解决,因为optional可以隐式类型转换成bool类型的
#include <iostream> #include <sw/redis++/redis++.h> using namespace std; using sw::redis::Redis; int main() { Redis redis("tcp://127.0.0.1:6379"); redis.flushall(); // 清空数据库,避免历史数据污染结果 redis.set("key1", "hello1"); // 返回值也和命令行一样,返回一个bool类型,后面的大部分命令的返回值都是和命令行一样的 redis.set("key2", "hello2"); redis.set("key3", "hello3"); auto value1 = redis.get("key1"); // 返回hello1 auto value4 = redis.get("key4"); if (value1) { cout << "value1 = " << value1.value() << endl; } if (value4) { cout << "value4 = " << value4.value() << endl; } return 0; }
2.2 exists,del
#include <iostream> #include <sw/redis++/redis++.h> using namespace std; using sw::redis::Redis; int main() { Redis redis("tcp://127.0.0.1:6379"); redis.flushall(); redis.set("hello", "world"); cout << redis.exists("hello") << endl; redis.del("hello"); cout << redis.exists("hello") << endl; // 同时exists也可以同时判断多个key是否存在 redis.set("hello1", "world1"); redis.set("hello2", "world2"); cout << redis.exists({"hello1", "hello2", "hello3"}) << endl; // 返回的数字为存在的key的个数 return 0; }
2.3 keys
keys这个操作和我们前面的操作还是有明显区别的,主要是体现在返回值上,因为keys返回的是“多个”值,先来看下keys的参数:
- 其中第一个参数pattern就是“筛选规则”
- 第二个参数类型为Output,是一个模板参数,是一个插入迭代器, 我们需要先准备好一个输出型参数,然后再创建一个插入迭代器指向容器的位置,就可以把keys获取到的结果通过插入迭代器插入到指定容器中,具体可以看代码实现
再创建一个头文件,方便打印容器的值:
util.hpp:
#pragma once #include <vector> #include <string> #include <iostream> template <typename T> inline void printContainer(const T &container) { for (const auto &elem : container) { std::cout << elem << std::endl; } }
hello.cc:
#include "util.hpp" #include <sw/redis++/redis++.h> using namespace std; using sw::redis::Redis; int main() { Redis redis("tcp://127.0.0.1:6379"); redis.flushall(); redis.set("hello1", "world1"); redis.set("hello2", "world2"); redis.set("hello3", "world3"); vector<string> result; auto it = std::back_inserter(result); // std::back_insert_iterator<std::vector<std::string>> it 这个就是插入迭代器 redis.keys("*", it); printContainer(result); return 0; }
下面我们深入了解一下插入迭代器:
STL中的五种迭代器类型:输入迭代器,输出迭代器,前向迭代器,双向迭代器,随机访问迭代器;而我们说的“插入迭代器”,也是一种“输出迭代器”
通常一个迭代器,主要是表示一个“位置”,插入迭代器则是一个“位置” + “动作”,库中提供的插入迭代器主要是三个:
- font_insert_iterator:区域的开头,往前面插入
- back_insert_iterator:区域的末尾,往后面插入
- insert_iterator:区间的任意位置,往该位置前面插入
我们一般不会直接去使用这几个迭代器去构造对象,因为构造函数写起来比较麻烦,所以我们一般会使用一些辅助的函数来进行构造,比如上面代码的 std::back_inserter(result); 这个就是辅助构造back_insert_iterator 的函数
所以对于插入迭代器来说,任何的*,++等操作都是啥也不干,它的核心操作就是赋值运算,也就是“ = ”,把另一个迭代器赋值给这个插入迭代器:
- 假设现在有两个迭代器,it 是插入迭代器,it2 是普通迭代器
- 当it = it2 时,就相当于 it 获取到 it2 的元素,然后按照 it 当前插入迭代器的“位置” 和 “动作”来进行执行插入操作
- 比如 it 是一个 back_insert_iterator ,就是把 it2 指向的元素插入到 it 指向的容器末尾
- 相当于调用了一次 push_back 尾插
问题:为什么不直接使用容器做参数?keys直接内部操作函数进行插入就可以了, 为啥还要通过迭代器绕一个大圈子呢?
解答:为了“解耦合”。再使用前,keys和容器两者都是互不可见的,此时双方都使用迭代器这样一个中间媒介进行交互,就可以形成“一对多”的情况,keys这个函数就可以搭配更多的容器来使用了,能提高代码的健壮性
2.4 expire,ttl
补充:
- Linux的睡眠函数是sleep(s),Windows的是Sleep(ms),系统函数是和系统相关的,同样的功能,在不同系统是可能是完全不同的函数,所以更好的选择是使用标准库的函数,C++也提供睡眠函数:thread -> sleep_for
- c++11开始针对字面值常量做了扩充,比如“ 1000L ”就是long long类型,“ 1.0f ”就是float类型,“ 1s ”就是seconds类型,毫秒就加上 ms 后缀
#include "util.hpp" #include <sw/redis++/redis++.h> #include <chrono> #include <thread> #include <unistd.h> using namespace std; using sw::redis::Redis; using namespace std::chrono_literals; // 字面值常量的命名空间 int main() { Redis redis("tcp://127.0.0.1:6379"); redis.flushall(); redis.set("hello", "world"); //redis.expire("hello", std::chrono::seconds(10)); // 可以使用库中的这个类型,也可以直接用设数字10,表示long long类型,但是为了可读性,建议使用库中的这个类型写 redis.expire("hello", 10s); //但是当c++11后就可以直接用字面值常量来代替上一条语句的类型了 for (int i = 10; i >= 0; i--) { cout << redis.ttl("hello") << endl; // sleep(1); std::this_thread::sleep_for(1s); // 字面值常量 } return 0; }
2.5 type
#include "util.hpp" #include <sw/redis++/redis++.h> using namespace std; using sw::redis::Redis; int main() { Redis redis("tcp://127.0.0.1:6379"); redis.flushall(); redis.set("hello1", "world1"); cout << redis.type("hello1") << endl; // 打印string redis.lpush("hello2", "222"); cout << redis.type("hello2") << endl; // 打印list redis.hset("hello3", "aaa", "111"); cout << redis.type("hello3") << endl; // 打印hash return 0; }
三,使用string命令
Redis远程字典服务器(4)—— string类型详解_redis的string类型-CSDN博客
3.1 string的set和get
用法其实和上面的差不多
#include "util.hpp" #include <sw/redis++/redis++.h> using namespace std; using sw::redis::Redis; int main() { Redis redis("tcp://127.0.0.1:6379"); redis.flushall(); redis.set("hello1", "world1"); auto value = redis.get("hello1"); if (value) { cout << value.value() << endl; } redis.set("hello1", "value1"); // 可以对key进行修改 value = redis.get("hello1"); if (value) { cout << value.value() << endl; } return 0; }
3.2 set的NX 和 EX
#include "util.hpp" #include <sw/redis++/redis++.h> using namespace std; using sw::redis::Redis; using namespace std::chrono_literals; int main() { Redis redis("tcp://127.0.0.1:6379"); redis.flushall(); // set 的重载版本中,没有单独提供NX 和 XX 的版本,必须搭配过期时间的版本来使用 redis.set("hello1", "world1", 0s, sw::redis::UpdateType::NOT_EXIST); auto value = redis.get("hello1"); if (value) { cout << value.value() << endl; } return 0; }
3.3 mset,mget
mget返回的是Optional类型,所以需要改一下util.hpp头文件:
#pragma once #include <vector> #include <string> #include <iostream> template <typename T> inline void printContainer(const T &container) { for (const auto &elem : container) { std::cout << elem << std::endl; } } template <typename T> inline void printContainerOptional(const T &container) { for (const auto &elem : container) { if (elem) // optional可能为无效值,所以需要先判断一下 { std::cout << elem.value() << std::endl; } else { std::cout << "元素无效" << std::endl; } } }
hello.cc:
#include "util.hpp" #include <sw/redis++/redis++.h> using namespace std; using sw::redis::Redis; using namespace std::chrono_literals; int main() { Redis redis("tcp://127.0.0.1:6379"); redis.flushall(); // 写法①:使用初始化列表一次性描述多个键值对 redis.mset({make_pair("key1", "111"), make_pair("key2", "222"), make_pair("key3", "333")}); // 写法②:先把多个键值对提前组织到一个容器中,以迭代器的形式告诉mset vector<pair<string, string>> keys = { {"key4", "444"}, {"key5", "555"}, {"key6", "666"}}; redis.mset(keys.begin(), keys.end()); // mget获取多个key的value vector<sw::redis::OptionalString> result; auto it = std::back_inserter(result); redis.mget({"key1", "key2", "key3", "key4", "key5", "key6"}, it); printContainerOptional(result); return 0; }
3.4 getrange,setrange
#include "util.hpp" #include <sw/redis++/redis++.h> using namespace std; using sw::redis::Redis; using namespace std::chrono_literals; int main() { Redis redis("tcp://127.0.0.1:6379"); redis.flushall(); redis.set("key", "1234567890"); string result = redis.getrange("key", 2, 5); // getrange获取字符串的一部分,返回的是string,如果是空的话也返回空字符串 cout << result << endl; redis.setrange("key", 2, "abc"); result = redis.getrange("key", 0, -1); cout << result << endl; return 0; }
3.5 incr,decr
#include "util.hpp" #include <sw/redis++/redis++.h> using namespace std; using sw::redis::Redis; using namespace std::chrono_literals; int main() { Redis redis("tcp://127.0.0.1:6379"); redis.flushall(); redis.set("key", "10"); redis.incr("key"); string result = redis.getrange("key", 0, -1); cout << result << endl; redis.decr("key"); result = redis.getrange("key", 0, -1); cout << result << endl; return 0; }
注意:
- incr的decr得到的是long long类型,get得到的是 OptionalString 类型,我们一般使用long long类似更多一些,因为数字比较好操作,而OptionalString要进行计算还得手动转成数字
四,使用list命令
Redis远程字典服务器(6) —— list类型详解-CSDN博客
4.1 lpush,lrange
#include "util.hpp" #include <sw/redis++/redis++.h> using namespace std; using sw::redis::Redis; using namespace std::chrono_literals; int main() { Redis redis("tcp://127.0.0.1:6379"); redis.flushall(); // lpush插入元素 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> result; auto it = back_inserter(result); redis.lrange("key", 0, -1, it); printContainer(result); return 0; }
4.2 rpush,lpop,rpop
rpush和前面的lpush是一样的:
#include "util.hpp" #include <sw/redis++/redis++.h> using namespace std; using sw::redis::Redis; using namespace std::chrono_literals; int main() { Redis redis("tcp://127.0.0.1:6379"); redis.flushall(); // lpush插入元素 redis.rpush("key", {"1", "2", "3", "4"}); // 初始化列表插入多个元素 auto result = redis.rpop("key"); // 尾删 if (result) { cout << "rpop尾删的元素为:" << result.value() << endl; } result = redis.lpop("key"); // 头删 if (result) { cout << "lpop头删的元素为:" << result.value() << endl; } // lrange 获取列表中的元素 vector<string> value; auto it = back_inserter(value); redis.lrange("key", 0, -1, it); printContainer(value); return 0; }
4.3 blpop,brpop
两个操作是一样的,所以我们只演示一个就好了,就是当列表为空时,就阻塞住,所以我们主要是来演示一下它的阻塞效果。
先来看下blpop的返回值类型:
- OptionalStringPair 类型其实就是 optional 里面包裹了一个 pair, pair 里面是 两个 string
- blpop要返回的内容是两个部分:①当前被删除的元素 ②这个元素属于那个list,因为 blpop 可以同时监听多个 list
- blpop 还可以设定超时时间,如果超过了时间,blpop 直接返回一个无效值
#include "util.hpp" #include <sw/redis++/redis++.h> using namespace std; using sw::redis::Redis; using namespace std::chrono_literals; int main() { Redis redis("tcp://127.0.0.1:6379"); redis.flushall(); while (true) { auto result = redis.blpop({"key", "key2", "key3"}); // 最后面也可以加上超时时间 if (result) { std::cout << "key: " << result.value().first << endl; std::cout << "elem: " << result->second << endl; // 对于 std::optional 类型,也可以直接使用 " -> ",来访问它内部包含的元素的成员 } } return 0; }
4.4 llen
#include "util.hpp" #include <sw/redis++/redis++.h> using namespace std; using sw::redis::Redis; using namespace std::chrono_literals; int main() { Redis redis("tcp://127.0.0.1:6379"); redis.flushall(); redis.lpush("key", {"111", "222", "333", "444"}); long long len = redis.llen("key"); cout << len << endl; return 0; }
五,set命令
Redis远程字典服务器(7)—— set类型详解_redis set add-CSDN博客
5.1 sadd,smembers
#include "util.hpp" #include <sw/redis++/redis++.h> #include <set> using namespace std; using sw::redis::Redis; using namespace std::chrono_literals; int main() { Redis redis("tcp://127.0.0.1:6379"); redis.flushall(); redis.sadd("key", "111"); // 一次添加一个元素 redis.sadd("key", {"222", "333", "444"}); // 一次添加多个元素 std::set<string> elems = {"555", "666", "777"}; redis.sadd("key", elems.begin(), elems.end()); // 用容器迭代器添加多个元素 // smembers获取set中的元素 set<string> result; //会报错,原因见后面“注意” vector<string> result; auto it = back_inserter(result); redis.smembers("key", it); printContainer(result); return 0; }
注意:
按一般情况来说,使用 set 来保存 smembers 的返回值会更好,但是将vector换成set之后,会报错 :
- 提示说 set 没有push_back 操作,这是对的,set 本来就没有push_back 的操作
- 这是因为我们前面用的插入迭代器是 back_inserter,back_insert_iterator 是把“末尾位置” 和 “push_back ” 这俩操作绑定在一起的
- 但是set没有push_back方法,set的插入操作是 insert ,而与insert对应的是insert_iterator
#include "util.hpp" #include <sw/redis++/redis++.h> #include <set> using namespace std; using sw::redis::Redis; using namespace std::chrono_literals; int main() { Redis redis("tcp://127.0.0.1:6379"); redis.flushall(); redis.sadd("key", "111"); // 一次添加一个元素 redis.sadd("key", {"222", "333", "444"}); // 一次添加多个元素 std::set<string> elems = {"555", "666", "777"}; redis.sadd("key", elems.begin(), elems.end()); // 用容器迭代器添加多个元素 // smembers获取set中的元素 set<string> result; // vector<string> result; // 由于set里的元素顺序是固定的,指定一个end()还是begin(),都是可以的 auto it = inserter(result, result.end()); redis.smembers("key", it); printContainer(result); return 0; }
5.2 sismember,scard,spop
#include "util.hpp" #include <sw/redis++/redis++.h> #include <set> using namespace std; using sw::redis::Redis; using namespace std::chrono_literals; int main() { Redis redis("tcp://127.0.0.1:6379"); redis.flushall(); redis.sadd("key", {"111", "222", "333", "444"}); // 一次添加多个元素 bool result1 = redis.sismember("key", "111"); // 判断值是否存在,返回值为bool cout << result1 << endl; auto result2 = redis.spop("key"); // 返回值也是OptionalString,返回被删除的元素,是随机删除 if (result2) { cout << result2.value() << endl; } long long result3 = redis.scard("key"); // 返回元素个数 cout << result3 << endl; return 0; }
5.3 sinter,sinterstore
求交集,并集,差集,这三个步骤其实都一样,所以只介绍一个就可以辣~
#include "util.hpp" #include <sw/redis++/redis++.h> #include <set> using namespace std; using sw::redis::Redis; using namespace std::chrono_literals; int main() { Redis redis("tcp://127.0.0.1:6379"); redis.flushall(); redis.sadd("key1", {"111", "222", "333"}); redis.sadd("key2", {"222", "333", "444"}); set<string> result1; auto it1 = std::inserter(result1, result1.end()); redis.sinter({"key1", "key2"}, it1); // 使用初始化列表一次搞进多个key,第二个参数是插入迭代器 printContainer(result1); cout << "----------" << endl; redis.sinterstore("key3", {"key1", "key2"}); set<string> result2; auto it2 = inserter(result2, result2.end()); redis.smembers("key3", it2); printContainer(result2); return 0; }
六,使用hash命令
Redis远程字典服务器(5) —— hash类型详解_hash使用-CSDN博客
6.1 hset,hget
#include "util.hpp" #include <sw/redis++/redis++.h> using namespace std; using sw::redis::Redis; using namespace std::chrono_literals; int main() { Redis redis("tcp://127.0.0.1:6379"); redis.flushall(); redis.hset("key", "f1", "111"); // 插入一个 redis.hset("key", make_pair("f2", "222")); // 使用pair插入 redis.hset("key", {make_pair("f3", "333"), make_pair("f4", "444")}); // 使用初始化列表插入多个,初始化列表里面每个元素都是pair类型 vector<pair<string, string>> fields = { make_pair("f5", "555"), make_pair("f6", "666")}; redis.hset("key", fields.begin(), fields.end()); // 通过容器迭代器插入 auto result = redis.hget("key", "f1"); if (result) { cout << result.value() << endl; } return 0; }
6.2 hexists,hdel,hlen
#include "util.hpp" #include <sw/redis++/redis++.h> using namespace std; using sw::redis::Redis; using namespace std::chrono_literals; int main() { Redis redis("tcp://127.0.0.1:6379"); redis.flushall(); redis.hset("key", "f1", "111"); redis.hset("key", "f2", "222"); redis.hset("key", "f3", "333"); bool result1 = redis.hexists("key", "f1"); // 判断field-value是否存在,返回值为bool cout << result1 << endl; long long result2 = redis.hdel("key", {"f1", "f2"}); // 删除field,可以一次删多个返回值为long long,表示删除的个数 cout << result2 << endl; long long result3 = redis.hlen("key"); cout << result3 << endl; return 0; }
6.3 hkeys,hvals
#include "util.hpp" #include <sw/redis++/redis++.h> using namespace std; using sw::redis::Redis; using namespace std::chrono_literals; int main() { Redis redis("tcp://127.0.0.1:6379"); redis.flushall(); redis.hset("key", "f1", "111"); redis.hset("key", "f2", "222"); redis.hset("key", "f3", "333"); vector<string> fields; auto it1 = back_inserter(fields); redis.hkeys("key", it1); // 获取hash类型的所有fields printContainer(fields); vector<string> values; auto it2 = back_inserter(values); redis.hvals("key", it2); printContainer(values); return 0; }
6.4 hmset,hmget
#include "util.hpp" #include <sw/redis++/redis++.h> using namespace std; using sw::redis::Redis; using namespace std::chrono_literals; int main() { Redis redis("tcp://127.0.0.1:6379"); redis.flushall(); redis.hmset("key", {make_pair("f1", 111), make_pair("f2", 222)}); vector<pair<string, string>> pairs = {make_pair("f3", "333"), make_pair("f4", "444")}; redis.hmset("key", pairs.begin(), pairs.end()); vector<string> values; auto it = back_inserter(values); redis.hmget("key", {"f1", "f2", "f3", "f4"}, it); printContainer(values); return 0; }
七,使用zset命令
Redis远程字典服务器(8)—— zset类型详解_redis zset 性能-CSDN博客
7.1 zadd,zrange
注意:
zrange支持两种风格的插入迭代器:
- 只查询 member ,不带 score
- 查询 member,带上score
关键就是看插入迭代器指向的容器的类型:
- 指向的容器只有一个string,就是只查询 member
- 指向的容器包含的是pair,里面有string 和 double ,就是查询member带上score
#include "util.hpp" #include <sw/redis++/redis++.h> using namespace std; using sw::redis::Redis; using namespace std::chrono_literals; int main() { Redis redis("tcp://127.0.0.1:6379"); redis.flushall(); redis.zadd("key", "hello1", 99.5); // 一次添加一个 redis.zadd("key", {make_pair("hello2", 95), make_pair("hello3", 97)}); // 初始化列表一次添加多个 vector<pair<string, double>> members = { make_pair("hello4", 90), make_pair("hello5", 85)}; redis.zadd("key", members.begin(), members.end()); // 通过容器迭代器插入多个 vector<string> members1; // 只查member auto it1 = back_inserter(members1); redis.zrange("key", 0, -1, it1); printContainer(members1); cout << "----------" << endl; vector<pair<string, double>> members2; // 查member带score auto it2 = back_inserter(members2); redis.zrange("key", 0, -1, it2); printContainerPair(members2); return 0; }
7.2 zcard,zrem
#include "util.hpp" #include <sw/redis++/redis++.h> using namespace std; using sw::redis::Redis; using namespace std::chrono_literals; int main() { Redis redis("tcp://127.0.0.1:6379"); redis.flushall(); redis.zadd("key", "hello1", 91); redis.zadd("key", "hello2", 92); redis.zadd("key", "hello3", 93); long long result = redis.zcard("key"); // 获取元素个数 cout << result << endl; redis.zrem("key", "hello1"); // 删除的三个重载和zadd一样,单个删除,初始化列表和容器迭代器删除多个 result = redis.zcard("key"); cout << result << endl; return 0; }
7.3 zscore,zrank
#include "util.hpp" #include <sw/redis++/redis++.h> using namespace std; using sw::redis::Redis; using namespace std::chrono_literals; int main() { Redis redis("tcp://127.0.0.1:6379"); redis.flushall(); redis.zadd("key", "hello1", 91); redis.zadd("key", "hello2", 92); redis.zadd("key", "hello3", 93); auto score = redis.zscore("key", "hello1"); // 查询分数 // zscore的返回值是OptionalDouble,这个其实和OptionalString是差不多的,都可以表示无效值 if (score) { cout << score.value() << endl; } auto rank = redis.zrank("key", "hello3"); // 查询元素的排序,或者是下标 // 返回值是OptionalLongLong,也是老样子 if (rank) { cout << rank.value() << endl; } return 0; }
八,小结
- redis-plus-plus 提供的各种函数,和我们之前学过的 redis 命令是匹配的
- redis-plus-plus的接口风格的设计是非常统一的,当一个函数参数需要传递多个值的时候,都是支持初始化列表或者一对迭代器的方式来进行实现的
- 当一个函数的返回值需要表示多个数据的时候,也往往会借助插入迭代器,来实现往一个容器中添加元素的效果
- 当某些场景涉及到无效值的时候,往往会搭配 std::optional 系列的类型来进行使用