redis系列



前言

本文将介绍如何使用编程方式操作Redis。与上篇通过Redis命令行操作不同,实际开发中通常需要通过代码调用Redis API来完成操作。


一、环境准备

1.1、安装 redis-plus-plus

C++ 操作 Redis 有多种库可供选择,其中 redis-plus-plus 是我们的推荐方案。该库功能全面且接口设计简洁易用。

前置依赖:redis-plus-plus 基于 hiredis 开发,需先安装 hiredis

# Ubuntu 系统安装 hiredis
sudo apt install libhiredis-dev

获取 redis-plus-plus 源码:

git clone https://github.com/sewenew/redis-plus-plus.git

编译安装步骤:

cd redis-plus-plus
mkdir build && cd build
cmake ..
make 
sudo make install

1.2、验证redis-plus-plus的功能可用性

#include <iostream>
#include <sw/redis++/redis.h>
#include <unordered_map>
#include <string>
using namespace std;

int main() {
    // 创建Redis连接实例,指定服务器地址和端口(需确保IP可达)
    sw::redis::Redis redis("tcp://127.0.0.1:6379");
    
    // 测试Redis服务连通性
    string result = redis.ping();
    cout << result << endl;
    
    return 0;
}

在这里插入图片描述

二、C++客户端操作Redis命令

这些命令在上篇文章中已有详细介绍,本文将简单介绍。

2.1、通用命令

2.1.1、get和set命令

void test1(sw::redis::Redis&redis){
    //清空之前的key
    redis.flushdb();

    //通过set设置key-value
    redis.set("key1","111");
    redis.set("key2","222");
    redis.set("key3","333");

    //通过get获取key对应的value
    auto value1=redis.get("key1");
    if(value1){
        cout<<"value1: "<<value1.value()<<endl;
    }
    auto value2=redis.get("key2");
    if(value2){
        cout<<"value2: "<<value2.value()<<endl;
    }
    auto value3=redis.get("key3");
    if(value3){
        cout<<"value3: "<<value3.value()<<endl;
    }
    auto value4=redis.get("key4");
    if(value4){
        cout<<"value4: "<<value4.value()<<endl;
    }
    
}

get函数返回的sw::redis::OptionalString类型支持隐式转换为布尔值,若不进行判断直接获取value会抛异常

在这里插入图片描述

2.1.2、exists/del命令

//exists/del
void test2(sw::redis::Redis&redis){
    //清空之前的key
    redis.flushdb();

    //通过set设置key-value
    redis.set("key1","111");
    redis.set("key2","222");
    redis.set("key3","333");
    
    //使用exists判断key是否存在
    auto ret=redis.exists("key1");
    cout<<ret<<endl;
    ret=redis.exists("key3");
    cout<<ret<<endl;
    ret=redis.exists("key4");
    cout<<ret<<endl;
    //查找多个key
    ret=redis.exists({"key1","key2","key3","key4"});
    cout<<ret<<endl;

    //del删除key
    ret=redis.del("key");
    cout<<ret<<endl;
    ret=redis.del("key1");
    cout<<ret<<endl;
    ret=redis.del({"key1","key2","key3","key4"});
    cout<<ret<<endl;

    //验证结果
    ret=redis.exists({"key1","key2","key3","key4"});
    cout<<ret<<endl;
    
}

在这里插入图片描述

操作结果均显示成功处理了对应数量的键值

2.1.3、keys

//keys
void test3(sw::redis::Redis&redis){
    //清空之前的key
    redis.flushdb();

    //通过set设置key-value
    redis.set("key1","111");
    redis.set("key2","222");
    redis.set("key3","333");
    redis.set("key4","444");
    redis.set("key5","555");
    redis.set("key6","666");
    
    //keys获取key
    vector<string> container;
    //这里涉及到了C++插入迭代器,C++部分介绍过了
    auto it=std::back_inserter(container);
    redis.keys("*",it);
    printContainer(container);
}

在这里插入图片描述

2.1.4、expire/ttl命令

//expire/ttl
void test4(sw::redis::Redis&redis){
    //引入这个可以使用字面长量
    using namespace std::chrono_literals;
    //清空之前的key
    redis.flushdb();

    //通过set设置key-value
    redis.set("key1","111");
    //设置有效时间
    //10s字面常量,也可以使用ms
    redis.expire("key1",10s);
    redis.expire("key1",std::chrono::seconds(10s));
    std::this_thread::sleep_for(5s);
    //查询有效时间
    auto ret=redis.ttl("key1");
    cout<<"有效时间:"<<ret<<endl;
    std::this_thread::sleep_for(5s);
    cout<<redis.exists("key1")<<endl;
}

在这里插入图片描述

2.1.5、type命令

//type
void test5(sw::redis::Redis&redis){

    //清空之前的key
    redis.flushdb();
    redis.set("key1","111");
    auto ret=redis.type("key1");
    cout<<"key1:"<<ret<<endl;
    redis.lpush("key2","222","333");
    ret=redis.type("key2");
    cout<<"key2:"<<ret<<endl;
    redis.hset("key3",{"姓名","张三","年龄","18"});
    ret=redis.type("key3");
    cout<<"key3:"<<ret<<endl;
    redis.sadd("key4",{"唱","跳","rapp"});
    ret=redis.type("key4");
    cout<<"key4:"<<ret<<endl;
    redis.zadd("key5","张三",80);
    ret=redis.type("key5");
    cout<<"key5:"<<ret<<endl;
    ret=redis.ttl("key1");
}

在这里插入图片描述

三、常用数据类型命令

3.1、string类型

3.1.1、mset/mget命令

//mget/mset
void test6(sw::redis::Redis&redis){

    //清空之前的key
    redis.flushdb();
    
    //借助容器存储
    vector<std::pair<string,string>> container{
        {"key1","111"},
        {"key2","222"},
        {"key3","333"}
    };
    redis.mset(container.begin(),container.end());

    //直接存储
    redis.mset({std::make_pair("key4","444"),std::make_pair("key5","555")});

    //mget
    //sw::redis::OptionalString:表示可能有值,可能没值
    vector<sw::redis::OptionalString> result;
    auto it=std::back_inserter(result);
    redis.mget({"key1","key2","key3","key4","key7"},it);
    printContainerOptionalString(result);
}

在这里插入图片描述

3.1.2、setrange/getrange命令

//setrange/getrange
void test7(sw::redis::Redis&redis){

    //清空之前的key
    redis.flushdb();
    redis.set("key1","abcdefghigklm");
    //获取指定范围数据
    string ret=redis.getrange("key1",0,2);
    cout<<ret<<endl;
    ret=redis.getrange("key1",1,3);
    cout<<ret<<endl;
    
    //修改指定范围的数据
    redis.setrange("key1",2,"1111");
    auto value=redis.get("key1");
    cout<<value.value()<<endl;

}

在这里插入图片描述

3.1.3、incr/decr命令

//incr/decr
void test8(sw::redis::Redis&redis){

    //清空之前的key
    redis.flushdb();
    redis.set("key1","100");
    //获取指定范围数据
    auto ret=redis.incr("key1");
    cout<<ret<<endl;
    ret=redis.decr("key1");
    cout<<ret<<endl;
}

在这里插入图片描述

get获取的结果是OptionalString类型,incr/decr返回的则是LongLong类型

3.2、list类型

3.2.1、lpush/lrange命令

void test9(sw::redis::Redis&redis){
    //清空之前的key
    redis.flushdb();
    
    //插入单个单元
    redis.lpush("key1","111");

    //插入一组数据,基于初始化列表
    redis.lpush("key1",{"222","333","444","555"});

    //插入一组数据,基于迭代器
    vector<string> container={
        "666",
        "777",
        "888"
    };
    redis.lpush("key1",container.begin(),container.end());

    //获取指定范围的元素
    vector<string> values;
    auto it=std::back_inserter(values);
    redis.lrange("key1",0,-1,it);
    printContainer(values);
}

在这里插入图片描述

3.2.3、blpop命令

在这里插入图片描述

using OptionalStringPair = std::optional<std::pair<std::string, std::string>>

BLPOP 函数支持同时操作多个键,因此返回结果采用键值对形式:first 字段标识被删除值所属的列表,second 字段记录被删除的具体值。

void test10(sw::redis::Redis&redis){
    using namespace std::chrono_literals;
    //清空之前的key
    redis.flushdb();
    auto ret=redis.blpop({"key1","key2","key3"},10s);//阻塞十秒,若不显示设置,则一直阻塞
    if(ret){
        cout<<"key:"<<ret.value().first<<endl;
        cout<<"value:"<<ret.value().second<<endl;
    }else{
        cout<<"list为空"<<endl;
    }
    
}

在这里插入图片描述

3.3、set类型

3.3.1、sadd/smembers命令

// sadd/smembers 操作测试
void test11(sw::redis::Redis& redis) {
    using namespace std::chrono_literals;
    
    // 清空数据库
    redis.flushdb();
    
    // 单值插入
    redis.sadd("key1", "111");
    
    // 通过初始化列表插入多个值
    redis.sadd("key1", {"222", "333", "444"});
    
    // 通过容器插入多个值
    vector<string> elems = {"555", "666", "777"};
    redis.sadd("key1", elems.begin(), elems.end());
    
    set<string> container;
    // 使用插入迭代器获取集合成员
    auto it = std::insert_iterator(container, container.end());
    redis.smembers("key1", it);
    printContainer(container);
}

在这里插入图片描述

3.3.2、sismember/spop/scard命令

//sismember/spop/scard命令
void test12(sw::redis::Redis&redis){
    using namespace std::chrono_literals;
    //清空之前的key
    redis.flushdb();
    //单键插入
    redis.sadd("key1","111");
    //插入一组数据,基于初始换列表
    redis.sadd("key1",{"222","333","444"});
    //插入一组数据,基于容器插入
    vector<string> elems{
        "555",
        "666",
        "777"
    };

    //判断元素是否存在于set中
    bool ret=redis.sismember("key1","111");
    cout<<ret<<endl;

    //对获取set中member的数量
    auto res=redis.scard("key1");
    cout<<res<<endl;

    //随机删除元素
    auto result=redis.spop("key1");
    cout<<result.value()<<endl;
    
}

在这里插入图片描述

3.3.3、sinter/sinterstore命令

void test13(sw::redis::Redis& redis) {
    redis.flushall();

    redis.sadd("key1", {"111", "222", "333"});
    redis.sadd("key2", {"111", "222", "444"});

    set<string> result;
    auto it = std::inserter(result, result.end());
    redis.sinter({"key1", "key2"}, it);

    printContainer(result);
    result.clear();
    cout<<"-------------------------------"<<endl;

    std::cout << "sinterstore" << std::endl;


    long long len = redis.sinterstore("key3", {"key1", "key2"});
    std::cout << "len: " << len << std::endl;

    redis.smembers("key3", it);

    printContainer(result);
}

在这里插入图片描述

3.3.4、hset/hget命令

void test14(sw::redis::Redis& redis) {
    std::cout << "hset 和 hget" << std::endl;
    redis.flushall();

    // 方式1:单字段设置
    redis.hset("key", "f1", "111");

    // 方式2:使用 pair 设置单字段
    redis.hset("key", std::make_pair("f2", "222"));

    // 方式3:一次性插入多个 field-value 对(初始化列表)
    redis.hset("key", {
        std::make_pair("f3", "333"),
        std::make_pair("f4", "444")
    });

    // 方式4:使用迭代器批量插入
    vector<std::pair<string, string>> fields = {
        std::make_pair("f5", "555"),
        std::make_pair("f6", "666")
    };
    redis.hset("key", fields.begin(), fields.end());

    // 获取字段值
    auto result = redis.hget("key", "f3");
    if (result) {
        std::cout << "result: " << result.value() << std::endl;
    } else {
        std::cout << "result 无效!" << std::endl;
    }
}

在这里插入图片描述

3.3.5、hexists/hdel命令

void test15(sw::redis::Redis& redis) {
    std::cout << "hexists" << std::endl;
    redis.flushall();

    redis.hset("key", "f1", "111");
    redis.hset("key", "f2", "222");
    redis.hset("key", "f3", "333");

    // 判断字段 "f4" 是否存在
    bool result = redis.hexists("key", "f4");
    std::cout << "result: " << result << std::endl;
    cout<<"----------------------------"<<endl;
    std::cout << "hdel" << std::endl;
    // 删除单个字段 "f1"
    long long ret = redis.hdel("key", "f1");
    std::cout << "ret: " << ret << std::endl;

    // 批量删除字段 "f2" 和 "f3"
    ret = redis.hdel("key", {"f2", "f3"});
    std::cout << "ret: " << ret << std::endl;

    // 获取哈希表中剩余的字段数量
    long long len = redis.hlen("key");
    std::cout << "len: " << len << std::endl;
}

在这里插入图片描述

3.3.6、hkeys/hvals命令

void test16(sw::redis::Redis& redis) {
    std::cout << "hkeys 和 hvals" << std::endl;
    redis.flushall();

    // 插入哈希字段
    redis.hset("key", "f1", "111");
    redis.hset("key", "f2", "222");
    redis.hset("key", "f3", "333");

    // 获取所有字段名(hkeys)
    vector<string> fields;
    auto itFields = std::back_inserter(fields);
    redis.hkeys("key", itFields);
    printContainer(fields);

    // 获取所有字段值(hvals)
    vector<string> values;
    auto itValues = std::back_inserter(values);
    redis.hvals("key", itValues);
    printContainer(values);
}

在这里插入图片描述

3.4、zset类型

3.4.1 zadd/zrange命令


void test16(sw::redis::Redis& redis) {
    redis.flushall();

    // 1. 单条插入
    redis.zadd("key", "吕布", 99);

    // 2. 初始化列表批量插入
    redis.zadd("key", {
        std::make_pair("赵云", 98),
        std::make_pair("典韦", 97)
    });

    // 3. vector 迭代器方式插入
    // 注意:zadd 要求分数是 double 类型,必须用 pair<string, double>
    vector<std::pair<string, double>> members = {
        std::make_pair("关羽", 95),
        std::make_pair("张飞", 93),
    };
    redis.zadd("key", members.begin(), members.end());

    // zrange 支持两种主要的风格:
    // 1. 只查询 member, 不带 score
    // 2. 查询 member 同时带 score
    // 关键就是看插入迭代器指向的容器的类型.
    // 指向的容器只是包含一个 string, 就是只查询 member
    // 指向的容器包含的是一个 pair, 里面有 string 和 double, 就是查询 member 同时带有 score

    // 第一种:只获取成员名称
    vector<string> memberResults;
    auto it = std::back_inserter(memberResults);
    redis.zrange("key", 0, -1, it);
    printContainer(memberResults);

    // 第二种:同时获取成员名称 + 分数
    vector<std::pair<string, double>> membersWithScore;
    auto it2 = std::back_inserter(membersWithScore);
    redis.zrange("key", 0, -1, it2);
    printContainerPair(membersWithScore);
}

在这里插入图片描述

更多推荐