Redis远程字典服务器(12)—— 使用C++操作Redis

avatar
作者
猴君
阅读量:0

目录

一,环境配置

1.1 介绍

1.2 安装hiredis 

1.3 安装redis-plus-plus 

1.4 连接服务器

二,使用通用命令

2.0 StringView,和OptionalString类型

2.1 set,get,

2.2 exists,del

2.3 keys

2.4 expire,ttl

2.5 type

三,使用string命令

3.1 string的set和get

3.2 set的NX 和 EX

3.3 mset,mget

3.4  getrange,setrange

3.5 incr,decr

​编辑

四,使用list命令

4.1 lpush,lrange

4.2 rpush,lpop,rpop

4.3 blpop,brpop

4.4 llen

五,set命令

5.1 sadd,smembers

5.2 sismember,scard,spop

5.3 sinter,sinterstore

六,使用hash命令

6.1 hset,hget

6.2 hexists,hdel,hlen

6.3 hkeys,hvals

6.4 hmset,hmget

七,使用zset命令

7.1 zadd,zrange

7.2 zcard,zrem

7.3 zscore,zrank

八,小结


一,环境配置

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函数的参数: 

  1. 这里用到了一个类型叫做StringView,这个类型定义在 sw::redis 命名空间中,是“只读”的,不能修改,因为可以针对“只读”的优势做很多优化工作,效率比std::string更高
  2. 在C++17标准库中,也提供了一个std::string_view,因为redis-plus-plus是支持C++11,C++14和C++17的,所以如果你的服务器支持更高版本的C++标准库,那么就会去使用C++库里的std::string_view,如果你的服务器只有低版本的库,那么只会去使用 sw::redis 里面的
  3. StringView中的各种操作和string类似,只不过ban了修改操作,同时也支持了一些只读方法

再看一下get函数的返回值: 

  1. 此处的OptionalString可以表示“非法值”或者“无效值” ,和命令行一样,如果key存在,就会返回value,如果key不存在,命令行就会返回一个“nil”,表示无效
  2. 而如果直接使用 std::string 来表示,不方便表示nil这个无效值;也可以使用指针返回nullptr来表示无效,但是返回指针又涉及到“内存归谁管”的类似问题
  3. 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,因为:

  1. 可能会消耗额外的运行时开销,对于追究极致性能的C++程序来说不太合适
  2. 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 的函数

所以对于插入迭代器来说,任何的*,++等操作都是啥也不干,它的核心操作就是赋值运算,也就是“ = ”,把另一个迭代器赋值给这个插入迭代器:

  1. 假设现在有两个迭代器,it 是插入迭代器,it2 是普通迭代器
  2. 当it = it2 时,就相当于 it 获取到 it2 的元素,然后按照 it 当前插入迭代器的“位置” 和 “动作”来进行执行插入操作
  3. 比如 it 是一个 back_insert_iterator ,就是把 it2 指向的元素插入到 it 指向的容器末尾
  4. 相当于调用了一次 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支持两种风格的插入迭代器:

  1. 只查询 member ,不带 score
  2. 查询 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 系列的类型来进行使用

    广告一刻

    为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!