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

news2024/11/14 14:55:13

目录

一,环境配置

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 系列的类型来进行使用

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

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

相关文章

【秋招笔试】8.25拼多多秋招-三语言题解

🍭 大家好这里是 春秋招笔试突围,一起备战大厂笔试 💻 ACM金牌团队🏅️ | 多次AK大厂笔试 | 编程一对一辅导 ✨ 本系列打算持续跟新 春秋招笔试题 👏 感谢大家的订阅➕ 和 喜欢💗 和 手里的小花花🌸 ✨ 笔试合集传送们 -> 🧷春秋招笔试合集 🍒 本专栏已收…

【测试】JMeter从入门到进阶

本文参考 Jmeter自动化测试工具从入门到进阶6小时搞定&#xff0c;适合手工测试同学学习_哔哩哔哩_bilibili JMeter介绍 JMeter 是 Apache 组织使用 Java 开发的一款测试工具&#xff1a; 1、可以用于对服务器、网络或对象模拟巨大的负载 2、通过创建带有断言的脚本来验证程序…

C3-80螺栓介绍及其特性

C3-80 螺栓作为马氏体不锈钢高强度紧固件的一员&#xff0c;在工程应用中扮演着重要角色。它不仅具有较高的强度&#xff0c;还拥有良好的耐腐蚀性能&#xff0c;适用于多种恶劣环境下的工业应用。 C3-80螺栓概述 C3-80螺栓是一种马氏体不锈钢材质的高强度紧固件&#xff0c;其…

电商数据怎么分析?电商数据接口助力电商运营中每日必看5个底层数据

数据分析充电站——深入探索中小企业数字化转型&#xff0c;专注提供各行业数据分析干货、分析技巧、工具推荐以及各类超实用分析模板&#xff0c;为钻研于数据分析的朋友们加油充电。 电商运营店铺涉及大量数据&#xff0c;包括用户行为、交易记录、库存信息等&#xff0c;如何…

【C++八股题整理】虚函数

C八股题整理 - 虚函数 虚函数虚函数的定义&#xff1f;C11引入的override和final关键字的作用&#xff1f;虚函数的实现原理&#xff1f;虚函数表&#xff08;vbtl&#xff09;和虚函数表指针&#xff08;vptr&#xff09;虚函数表、虚函数表指针的生成时期及存储位置&#xff…

JS常用事件示例

<!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta name"viewport" content"widthdevice-width, initial-scale1.0"> <title>JS函数中的事件</title> <…

钡铼技术BL196MQTT远程IO模块工业物联网应用

随着工业物联网&#xff08;IIoT&#xff09;的迅猛发展&#xff0c;工业设备之间的互联互通已成为推动产业升级的关键因素之一。在这个背景下&#xff0c;钡铼技术推出了一款名为BL196MQTT的远程IO模块&#xff0c;该模块专为工业自动化环境中的数据采集与控制而设计&#xff…

搭建深度神经网络(DNN)

利用 numpy 工具&#xff0c;手动搭建一个 DNN 深度神经网络。 定义网络结构 初始化模型参数 循环计算&#xff1a;前向传播/计算当前损失/反向传播/权值更新 1、初始化模型参数 对于一个包含L层的隐藏层深度神经网络&#xff0c;我们在初始化其模型参数的时候需要更灵活一点…

触想强固型工业显示器加速海上油气勘探开发

石油作为现代工业发展的主要能源&#xff0c;已成为国际间政治、经济博弈的重要工具。 一、行业发展背景 过去百年间&#xff0c;人类对陆地油气资源的勘探开发逐渐趋于饱和&#xff0c;而面对持续增长的全球能源需求&#xff0c;海洋勘探已成为当今油气能源角逐的主要“战场”…

Linux文件IO缓存

一、缓冲区大小对 I/O 系统调用性能的影响 总之&#xff0c;如果与文件发生大量的数据传输&#xff0c;通过采用大块空间缓冲数据&#xff0c;以及执行更少的 系统调用&#xff0c;可以极大地提高 I / O 性能 二、stdio 库的缓冲 当操作磁盘文件时&#xff0c;缓冲大块数据以…

合宙LuatOS产品规格书——Air700EAQ

Luat Air700EAQ是合宙的LTE Cat.1bis通信模块&#xff0c;采用移芯EC716E平台&#xff0c;支持LTE 3GPP Rel.13技术。 该模块专为满足小型化、低成本需求而设计&#xff0c;具备超小封装和极致成本优势。 Air700EAQ支持移动双模&#xff0c;内置丰富的网络协议&#xff0c;集…

Qt第二十章 数据库操作

文章目录 Qt操作数据库QSqlDataBaseQSqlQuery执行SQL语句 QSqlRecordQSqlField数据库模型QSqlQueryModelQSqlTableModelQSqlRelationalTableModel 编译MySql驱动msvc版本MySql客户端程序部署 Qt操作数据库 需要在cmakelist加上Sql模块 QSqlDataBase 可以通过静态成员查看支持的…

北京青蓝智慧科技:2024(第九届)世界物联网大会将于11月在京举行

2024年11月&#xff0c;北京将迎来第九届世界物联网大会的盛大启幕。 这一年度盛会由世界物联网大会、中国移动通信联合会、外交理事会携手举办&#xff0c;得到了世界绿色设计组织、世界物联网基金会等机构的大力支持。 大会的宗旨在于推动全球智能联网数字经济的创新进展&a…

Golang | Leetcode Golang题解之第373题查找和最小的K对数字

题目&#xff1a; 题解&#xff1a; func kSmallestPairs(nums1, nums2 []int, k int) (ans [][]int) {m, n : len(nums1), len(nums2)// 二分查找第 k 小的数对和left, right : nums1[0]nums2[0], nums1[m-1]nums2[n-1]1pairSum : left sort.Search(right-left, func(sum in…

Notion 使用详解——基础教程

《Notion 使用详解——基础教程》 一、Notion简介 Notion是一款集笔记、任务、数据库、wiki、知识库等功能于一体的生产力工具&#xff0c;其强大的模块化设计和高度自定义能力&#xff0c;使其成为个人和团队提高工作效率的理想选择。 二、基础操作 1. 创建页面&#xff1a;…

几个很棒的AI问题和精彩回答

这里有几个很棒的与AI相关的问题和精彩的回答&#xff0c;分享给大家 2024&#xff0c;怎么以10倍的速度设计AI产品&#xff1f; 回答嘉宾&#xff1a;Tidyread作者 根据产品定位&#xff0c;对整体风格进行定调 Tidyread 希望人们能从中建立资讯阅读的秩序感&#xff0c;所以…

阿里云对象存储OSS的前端直传-demo

原由 在项目里有时候会碰到比如上传文件相关的&#xff0c;一般都是后端提供个接口&#xff0c;然后我们上传的时候后端再传到阿里OSS或者其他服务商的对象存储&#xff0c;然后把最终的url拿到存起来或者返回给前端&#xff0c;这种方式其实在上传图片的频率不高的业务场景中…

电商数据接口助力电商数据分析||电商运营每日必看5个底层数据

数据分析充电站——深入探索中小企业数字化转型&#xff0c;专注提供各行业数据分析干货、分析技巧、工具推荐以及各类超实用分析模板&#xff0c;为钻研于数据分析的朋友们加油充电。 电商运营店铺涉及大量数据&#xff0c;包括用户行为、交易记录、库存信息等&#xff0c;如何…

Python测试之测试覆盖率统计

本篇承接上一篇 Python测试框架之—— pytest介绍与示例&#xff0c;在此基础上介绍如何基于pytest进行测试的覆盖率统计。 要在使用 pytest 进行测试时检测代码覆盖率&#xff0c;可以使用 pytest-cov 插件。这个插件是基于 coverage.py&#xff0c;它能帮助你了解哪些代码部…

【PySide6-QML】2. 添加菜单栏

文章目录 前言实现添加菜单栏添加菜单添加子菜单点击动作添加快捷键 前章回顾&#xff1a;【PySide6-QML】1. 创建新项目 前言 本文使用 MenuBar 添加工具菜单栏&#xff0c;Action 添加子菜单&#xff0c;并添加快捷键和动作回调。 实现 添加菜单栏 import QtQuick.Contr…