目录
[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)
[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)
[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)
[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)
[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)
[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好比是汇编语言
bashsudo 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函数的参数:
- 这里用到了一个类型叫做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,
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,因为:
- 可能会消耗额外的运行时开销,对于追究极致性能的C++程序来说不太合适
- 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;
}
}
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 的函数
所以对于插入迭代器来说,任何的*,++等操作都是啥也不干,它的核心操作就是赋值运算,也就是" = ",把另一个迭代器赋值给这个插入迭代器:
- 假设现在有两个迭代器,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 后缀
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;
}
}
}
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支持两种风格的插入迭代器:
- 只查询 member ,不带 score
- 查询 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 系列的类型来进行使用