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

目录

一,环境配置

[1.1 介绍](#1.1 介绍)

[1.2 安装hiredis](#1.2 安装hiredis)

[1.3 安装redis-plus-plus](#1.3 安装redis-plus-plus)

[1.4 连接服务器](#1.4 连接服务器)

二,使用通用命令

[2.0 StringView,和OptionalString类型](#2.0 StringView,和OptionalString类型)

[2.1 set,get,](#2.1 set,get,)

[2.2 exists,del](#2.2 exists,del)

[2.3 keys](#2.3 keys)

[2.4 expire,ttl](#2.4 expire,ttl)

[2.5 type](#2.5 type)

三,使用string命令

[3.1 string的set和get](#3.1 string的set和get)

[3.2 set的NX 和 EX](#3.2 set的NX 和 EX)

[3.3 mset,mget](#3.3 mset,mget)

[3.4 getrange,setrange](#3.4 getrange,setrange)

[3.5 incr,decr](#3.5 incr,decr)

​编辑

四,使用list命令

[4.1 lpush,lrange](#4.1 lpush,lrange)

[4.2 rpush,lpop,rpop](#4.2 rpush,lpop,rpop)

[4.3 blpop,brpop](#4.3 blpop,brpop)

[4.4 llen](#4.4 llen)

五,set命令

[5.1 sadd,smembers](#5.1 sadd,smembers)

[5.2 sismember,scard,spop](#5.2 sismember,scard,spop)

[5.3 sinter,sinterstore](#5.3 sinter,sinterstore)

六,使用hash命令

[6.1 hset,hget](#6.1 hset,hget)

[6.2 hexists,hdel,hlen](#6.2 hexists,hdel,hlen)

[6.3 hkeys,hvals](#6.3 hkeys,hvals)

[6.4 hmset,hmget](#6.4 hmset,hmget)

七,使用zset命令

[7.1 zadd,zrange](#7.1 zadd,zrange)

[7.2 zcard,zrem](#7.2 zcard,zrem)

[7.3 zscore,zrank](#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:

bash 复制代码
sudo apt install libhiredis-dev

Centos:

bash 复制代码
sudo yum install hiredis-devel.x86_64

1.3 安装redis-plus-plus

②接下来我们安装redis-plus-plus的本体,这个安装比较麻烦,只能通过源码编译去安装了,这里只演示Ubuntu环境下的安装:

首先是下载源码:

bash 复制代码
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好比是汇编语言

    bash 复制代码
    sudo apt install cmake

下面是编译的具体步骤,下面的各种操作最好都加上sudo或者直接切换成root用户

然后就可以直接使用make进行编译,需要的时间可能要一会儿,最后生成的动静态库如下:

后续写代码不一定能找到这里的库,所以推荐把这些库,拷贝到系统目录中,而且这步操作也不用我们自己搞,直接使用下面的命令即可:

bash 复制代码
make install

可以直接把内容拷贝到系统目录里了,包括链接等过程

很多C++中的库,都是需要编译安装的,而具体的操作大多类似

1.4 连接服务器

接下来就是使用redis-plus-plus连接服务器了,再使用ping命令,来检测一下连通性:

直接在自己认为的合适位置创建test目录,创建hello.cc源码,使用VSCode开始编写代码

首先要包含redis的入口头文件,一般放在下面目录中:

hello.cc代码如下:

cpp 复制代码
#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如下:

bash 复制代码
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,

cpp 复制代码
#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类型的

cpp 复制代码
#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

cpp 复制代码
#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

cpp 复制代码
#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

cpp 复制代码
#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 后缀
cpp 复制代码
#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

cpp 复制代码
#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

用法其实和上面的差不多

cpp 复制代码
#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

cpp 复制代码
#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头文件:

cpp 复制代码
#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

cpp 复制代码
#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

cpp 复制代码
#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

cpp 复制代码
#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

cpp 复制代码
#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是一样的:

cpp 复制代码
#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 直接返回一个无效值
cpp 复制代码
#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

cpp 复制代码
#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

cpp 复制代码
#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
cpp 复制代码
#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

cpp 复制代码
#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

求交集,并集,差集,这三个步骤其实都一样,所以只介绍一个就可以辣~

cpp 复制代码
#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

cpp 复制代码
#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

cpp 复制代码
#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

cpp 复制代码
#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

cpp 复制代码
#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
cpp 复制代码
#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

cpp 复制代码
#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

cpp 复制代码
#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 系列的类型来进行使用
相关推荐
别NULL4 小时前
机试题——疯长的草
数据结构·c++·算法
CYBEREXP20085 小时前
MacOS M3源代码编译Qt6.8.1
c++·qt·macos
yuanbenshidiaos5 小时前
c++------------------函数
开发语言·c++
yuanbenshidiaos5 小时前
C++----------函数的调用机制
java·c++·算法
tianmu_sama6 小时前
[Effective C++]条款38-39 复合和private继承
开发语言·c++
羚羊角uou6 小时前
【C++】优先级队列以及仿函数
开发语言·c++
姚先生976 小时前
LeetCode 54. 螺旋矩阵 (C++实现)
c++·leetcode·矩阵
FeboReigns6 小时前
C++简明教程(文章要求学过一点C语言)(1)
c语言·开发语言·c++
FeboReigns6 小时前
C++简明教程(文章要求学过一点C语言)(2)
c语言·开发语言·c++
264玫瑰资源库7 小时前
从零开始C++棋牌游戏开发之第二篇:初识 C++ 游戏开发的基本架构
开发语言·c++·架构