C++编写Redis客户端

目录

安装redis-plus-plus库

​编辑

编译C++redis客户端

redis的通用命令使用 

get/set

exists

del

keys

expire /ttl

type

string类型核心操作 

set和get

set带有超时时间

set带有NX

string带有XX

mset

mget

getrange和setrange

incr和decr 

list类型核心操作

lpush和lrange

 lpop和rpop

brpop

llen

set类型核心操作

sadd和smembers

sismember

 scard

spop

sinter

sinterstore

hash类型核心操作

hset和hget

 hexists和hdel和hlen

hmset和hmget

hkeys hvals

zset类型核心操作

zadd和zrange

zscore 

zrank


安装redis-plus-plus库

C++操作redis的库有很多,这里使用redis-plus-plus,我们需要在githb上下载。

github地址:GitHub - sewenew/redis-plus-plus: Redis client written in C++

github在国内的时候有时候会抽风,网不好,怎么呢?fq当然可以打开github,但fq是违法行为。

那有没有合法的方式打开呢?我们可以下载Watt Toolkit软件,这玩意其实就是一个游戏加速器。

 打开之后点击网络加速,这里面可以对github进行加速。

打开github后往下面翻,可以看到下载,要下载redis-plus-plus要先安装hiredis。

 安装hiredis

ubuntu

apt install libhiredis-dev

 

 安装redis-plus-plus

下载redis-plus-plus源

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

安装好源之后,按照下面的命令执行即可 

cd redis-plus-plus

mkdir build  // 创建一个 build、 目录是习惯用法,让编译临时生成的文件放在改目录下

cd build

cmake ..      // 生成makefile

make           // 进行编译

make install // 把库考到系统目录

cd ..

安装完毕后,头文件会装到 /usr/local/include/sw/的redis++。

编译C++redis客户端

要写redis代码需要包含一下头文件<sw/redis++/redis++.h>。

当我们创建好Redis对象之后,执行的各种命令,其实就是该类中调用各种方法即可。

#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <sw/redis++/redis++.h>

using  namespace std;


int main()
{
    // 创建redis对象的时候需要再构造函数中指定 redis 服务器的地址和端口 由于我们redis客户端和服务器在同一个主机,写本地回环即可
    sw::redis::Redis redis("tcp://127.0.0.1:6379"); // 如果是不同主机,就写对应主机的ip即可,6379是端口号
    // 测试是否连接成功 让客户端给服务器发送一个 ping 服务器会返回一个 PONG,通过返回值获取到
    string result = redis.ping();
    cout << result << endl;
    return 0;

}

使用makefile编译文件

编译程序的时候,需要引入库文件。

1. redis++自己的静态库  

我们可以使用find命令查找。

/usr/local/lib/libredis++.a

2.hiredis的静态库

/usr/lib/x86_64-linux-gnu/libhiredis.a 

3.线程库 

-pthread直接写就可以

hello:01.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 -f hello

redis的通用命令使用 

如果会使用redis的命令,那么使用C++编写redis客户端是非常简单的,很多都是接口都是一样的设计,命令的参数怎么写的的函数的参数就怎么传递就行。下面我并不会介绍每个函数的作用是啥,因为默认各位都是对命令了然于胸的,如果看到哪一个命令想不起来它的作用,随便上网查一下即可,或者是看下代码应该就能想起来,并且我不会把所有的命令都写出来,因为有些命令是非常简单的。

get/set


void test1(sw::redis::Redis &redis)
{
    cout << "get 和 set 的使用" << endl;

    // 1.清空一下数据库,以免影响我们使用
    redis.flushall();

    // 使用 set 设置 key
    redis.set("key1", "111");
    redis.set("key2", "222");
    redis.set("key3", "333");

    // 使用 get 获取 key
    // get的返回值是sw::redis::OptionalString类型
    auto value1 = redis.get("key1");
    // optional 可以隐式转换成 bool 类型,可以直接在 if中判断,如果是无效元素,就返回false
    // 使用之前判断是否存在
    if (value1)
    {
        // get的返回值是sw::redis::OptionalString类型,cout不支持该类型的运算符重载
        // 此处把sw::redis::OptionalString里面包含的值取出来就行,OptionalString只包含一个元素的容器
        // 可以使用value()方法取出来
        cout << "key1: " << value1.value() << endl;
    }

    auto value2 = redis.get("key2");
    if (value2)
    {
        cout << "key2: " << value2.value() << endl;
    }

    auto value3 = redis.get("key3");
    if (value3)
    {
        cout << "key3: " << value3.value() << endl;
    }

    // redis中并不存在key4
    auto value4 = redis.get("key4");
    if (value4)
    {
        cout << "key4: " << value4.value() << endl;
    }
}

 

exists

// exists
void test2(sw::redis::Redis &redis)
{
    cout << "exists" << endl;
    // 1.清空一下数据库,以免影响我们使用
    redis.flushall();

    // 使用 set 设置 key
    redis.set("key1", "111");

    // 存在的key
    // 返回值是一个long long类型,表示有几个key存在
    auto ret = redis.exists("key1");
    cout << ret << endl;
    // 不存在的key
    ret = redis.exists("key2");
    cout << ret << endl;
    // 判定多个 key
    redis.set("key3", "333");
    ret = redis.exists({"key1", "key2", "key3"});
    cout << ret << endl;
}

del

void test3(sw::redis::Redis &redis)
{
    cout << "del" << endl;
    // 1.清空一下数据库,以免影响我们使用
    redis.flushall();
    // 使用 set 设置 key
    redis.set("key1", "111");
    redis.set("key2", "222");

    // 删除一个key
    // 返回值是成功删除的个数
    auto ret = redis.del("key1");
    cout << ret << endl; // key1 存在返回值为1 表示删除1个
    // 删除多个key
    ret = redis.del({"key2", "key3"});
    cout << ret << endl; // key2 存在 key3 不存在 返回值2 表示删除2个

    // 判定被删除的key是否存在
    ret = redis.exists({"key1", "key2"}); // key1 key2都被删除了,因此存在个数0
    cout << ret << endl;
}

keys


void test4(sw::redis::Redis &redis)
{
    cout << "keys" << endl;
    // 1.清空一下数据库,以免影响我们使用
    redis.flushall();
    // 使用 set 设置 key
    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第一个参数匹配规则
    // keys第二个参数是一个"插入迭代器",需要先准备好一个保存结果的容器
    // 接下来在创建一个插入迭代器指向容器的位置,就可以吧keys获取到的结果依次通过刚才的插入迭代器插入到容器指定位置中
    vector<string> v;
    // 这个auto的类型是 back_inserter<std::vector<std::string>>
    // 此处的it就指向v的起始地址
    auto it = back_inserter(v);
    // 这个地方是把keys查询的所有结果插入到v这个容器中
    redis.keys("*", it);
    // keys查询的key都放到v这个容器中
    // 遍历vector,查看结果
    for (auto &elem : v)
    {
        cout << elem << endl;
    }
}

expire /ttl

void test5(sw::redis::Redis &redis)
{
    cout << "expire and ttl" << endl;
    // 1.清空一下数据库,以免影响我们使用
    redis.flushall();
    // 使用 set 设置 key
    redis.set("key1", "111");
    // 设置key的过期时间
    redis.expire("key1", 10);                  // 这里可以直接跟字面值表示10s
    redis.expire("key1", chrono::seconds(10)); // 建议使用chrono的库函数,更加直观

    // 先休眠一会再查看过期时间
    this_thread::sleep_for(chrono::seconds(5)); // 休眠5s

    // 获取key的过期时间
    long long time = redis.ttl("key1");
    cout << time << endl; // 5s后,key1的过期时间还剩5s
}

type

void test6(sw::redis::Redis &redis)
{
    cout << "type" << endl;
    // 1.清空一下数据库
    redis.flushall();
    // 2. string 类型
    redis.set("key1", "111");
    string type = redis.type("key1");
    cout << "key1: " << type << endl;
    // 3. list 类型
    redis.lpush("key2", "111");
    type = redis.type("key2");
    cout << "key2: " << type << endl;
    // 4. hash 类型
    redis.hset("key3", "field1", "111");
    type = redis.type("key3");
    cout << "key3: " << type << endl;
    // 5. set 类型
    redis.sadd("key4", "111");
    type = redis.type("key4");
    cout << "key4: " << type << endl;
    // 6. zset 类型
    redis.zadd("key5", "吕布", 99);
    type = redis.type("key5");
    cout << "key5: " << type << endl;
}

string类型核心操作 

set和get

void test1(sw::redis::Redis &redis)
{
    cout << "get 和 set 的使用" << endl;
    // 1.清空一下数据库,以免影响我们使用
    redis.flushall();

    redis.set("key", "111");
    // value是optionalString类型,需要使用value()方法取出来
    auto value = redis.get("key");
    if (value)
    {
        std::cout << "value: " << value.value() << std::endl;
    }

    // 修改key的value
    redis.set("key", "222");
    value = redis.get("key");
    if (value)
    {
        std::cout << "value: " << value.value() << std::endl;
    }

}

set带有超时时间

void test2(sw::redis::Redis &redis)
{
    cout << "set 带有超时时间" << endl;
    // 1.清空一下数据库,以免影响我们使用
    redis.flushall();

    // 给key设置超时时间为9s
    redis.set("key", "111", chrono::seconds(9));
    // 休息6s
    this_thread::sleep_for(chrono::seconds(6));
    // 获取超时时间
    auto time = redis.ttl("key");
    cout << "time: " << time << endl;
}

 

set带有NX

void test3(sw::redis::Redis &redis)
{
    cout << "set NX 和 XX" << endl;
    // 1.清空一下数据库,以免影响我们使用
    redis.flushall();

    // 包含该命名空间可以直接使用 0s 这样的字面值常量
    using namespace std::chrono_literals;

    // set的重载版本中,没有单独提供 NX 和 XX版本,必须搭配过期时间使用,不需要过期时间就设置为0s
    // 不存在就设定
    redis.set("key", "111", 0s, sw::redis::UpdateType::NOT_EXIST);

    auto value = redis.get("key");
    if (value)
    {
        cout << "value: " << value.value() << endl;
    }
    else
    {
        cout << "key 不存在!" << endl;
    }
}

void test3(sw::redis::Redis &redis)
{
    cout << "set NX 和 XX" << endl;
    // 1.清空一下数据库,以免影响我们使用
    redis.flushall();

    // 包含该命名空间可以直接使用 0s 这样的字面值常量
    using namespace std::chrono_literals;

    redis.set("key", "111");
    // set的重载版本中,没有单独提供 NX 和 XX版本,必须搭配过期时间使用,不需要过期时间就设置为0s
    // 上面已经设置了key,所有下面set不会设置成功
    redis.set("key", "222", 0s, sw::redis::UpdateType::NOT_EXIST);

    auto value = redis.get("key");
    if (value)
    {
        // 打印的结果是111
        cout << "value: " << value.value() << endl;
    }
    else
    {
        cout << "key 不存在!" << endl;
    }
}

string带有XX

void test3(sw::redis::Redis &redis)
{
    cout << "set NX 和 XX" << endl;
    // 1.清空一下数据库,以免影响我们使用
    redis.flushall();

    // 包含该命名空间可以直接使用 0s 这样的字面值常量
    using namespace std::chrono_literals;

    redis.set("key", "111");
    // set的重载版本中,没有单独提供 NX 和 XX版本,必须搭配过期时间使用,不需要过期时间就设置为0s
    // 已经存在才能设置成功,上面存在,因此可以设置成功
    redis.set("key", "222", 0s, sw::redis::UpdateType::EXIST);

    auto value = redis.get("key");
    if (value)
    {
        // 打印的结果是222
        cout << "value: " << value.value() << endl;
    }
    else
    {
        cout << "key 不存在!" << endl;
    }
}

mset

void test4(sw::redis::Redis &redis)
{
    cout << "mset" << endl;

    redis.flushall();

    // 第一种写法,使用初始化列表描述多个键值对
    redis.mset({make_pair("key1", "111"),
                make_pair("key2", "222")});
    // 第二种写法,把多个键值对提前放到容器中,以迭代器的形式告诉mset
    vector<pair<string, string>> keys;
    keys.push_back(make_pair("key3", "333"));
    keys.push_back(make_pair("key4", "444"));

    redis.mset(keys.begin(), keys.end());

    auto value = redis.get("key1");
    if (value)
    {
        cout << "value: " << value.value() << endl;
    }

    value = redis.get("key2");
    if (value)
    {
        cout << "value: " << value.value() << endl;
    }

    value = redis.get("key3");
    if (value)
    {
        cout << "value: " << value.value() << endl;
    }

    value = redis.get("key4");
    if (value)
    {
        cout << "value: " << value.value() << endl;
    }
}

mget

void test5(sw::redis::Redis &redis)
{
    cout << "mget" << endl;
    redis.flushall();

    vector<pair<string, string>> keys;
    keys.push_back(make_pair("key1", "111"));
    keys.push_back(make_pair("key2", "222"));
    keys.push_back(make_pair("key3", "333"));

    redis.mset(keys.begin(), keys.end());

    vector<sw::redis::OptionalString> values;
    auto it = std::back_inserter(values);


    redis.mget({"key1", "key2", "key3", "key4"}, it);

    for (auto &elem : values)
    {
        if (elem)
            cout << elem.value() << endl;
        else
            cout << "元素无效" << endl;
    }
}

getrange和setrange


void test6(sw::redis::Redis &redis)
{
    cout << "getrange and setrange " << endl;
    redis.flushall();

    redis.set("key", "hello world");
    // 获取到下标2-5的字符
    string ret = redis.getrange("key", 2, 5);
    cout << "ret: " << ret << endl;

    // 将下标2后面的字符修改为music
    redis.setrange("key", 2, "music");

    auto value = redis.get("key");
    if (value)
        cout << "value: " << value.value() << endl;
}

incr和decr 

void test7(sw::redis::Redis &redis)
{
    cout << "incr end decr" << endl;
    redis.flushall();

    redis.set("key", "100");

    // 自增
    long long ret = redis.incr("key");
    auto value = redis.get("key");
    if (value)
        cout << "value: " << value.value() << endl;

    // 自减
    ret = redis.decr("key");
    value = redis.get("key");
    if (value)
        cout << "value: " << value.value() << endl;
}

list类型核心操作

lpush和lrange

void test1(Redis &redis)
{
    cout << "lpush 和 lrange" << endl;
    redis.flushall();

    //  插入单个元素
    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> results;
    auto it = std::back_inserter(results);
    redis.lrange("key", 0, -1, it);

    for (auto &elem : results)
    {
        cout << elem << endl;
    }
}

lpush是头插,因此越后面的元素在前面。 rpush和lpush的用法是一模一样的,这里就不介绍了。

 lpop和rpop

void test3(Redis &redis)
{
    cout << "lpop 和 rpop" << endl;
    redis.flushall();
    // 构造一个 list
    redis.rpush("key", {"111", "222", "333", "444", "555"});

    // lpop 从左边弹出一个元素
    auto value = redis.lpop("key");
    if (value)
        cout << value.value() << endl;
    // rpop 从右边弹出一个元素
    value = redis.rpop("key");
    if (value)
        cout << value.value() << endl;
}

brpop

阻塞删除一个key

void test4(Redis &redis)
{
    cout << "blpop" << endl;
    redis.flushall();

    // key不存在一定会阻塞
    // 返回值是 OptionalStringPair 是一个pair
    // pair的first是来自哪一个key
    // pair的second是哪个元素被删了
    auto result = redis.blpop("key");
    if (result)
    {
        cout << "key:" << result.value().first << endl;
        cout << "elem:" << result.value().second << endl;
    }
    else
    {
        cout << "result 无效!" << endl;
    }
}

阻塞删除多个key 并设置超时时间

void test4(Redis &redis)
{
    using namespace std::chrono_literals;
    cout << "blpop" << endl;
    redis.flushall();

    // key不存在一定会阻塞
    // 返回值是 OptionalStringPair 是一个pair
    // pair的first是来自哪一个key
    // pair的second是哪个元素被删了
    auto result = redis.blpop({"key", "key1", "key2"}, 10s);
    if (result)
    {
        cout << "key:" << result.value().first << endl;
        cout << "elem:" << result.value().second << endl;
    }
    else
    {
        cout << "result 无效!" << endl;
    }
}

llen


void test5(Redis &redis)
{
    cout << "llen" << endl;
    redis.flushall();

    redis.lpush("key", {"111", "222", "333", "444", "555"});
    long long len = redis.llen("key");
    cout << "len: " << len << endl;
}

redis-plus-plus这个库,接口风格设计是非常统一的。

当一个函数,参数需要传递多个值的时候,往往都是支持初始化列表或者是一对迭代器的方式来进行实现。

当一个函数,返回值需要表示多个数据的时候,往往也会借助插入迭代器,来实现往一个容器中添加元素的效果。

当某些场景涉及到无效值的时候,往往会搭配std::optional来进行使用。

set类型核心操作

sadd和smembers

void test1(Redis &redis)
{
    cout << "sadd 和 smembers" << endl;
    redis.flushall();

    // 一次添加一个元素
    redis.sadd("key", "111");
    // 一次添加多个元素 使用初始化列表
    redis.sadd("key", {"222", "333", "444"});

    // 一次添加多个元素 使用迭代器
    // 使用什么容器都可以,set、vector、list...
    set<string> elems = {"555", "666", "777"};
    redis.sadd("key", elems.begin(), elems.end());

    // 获取集合中的所有元素
    vector<string> result;
    auto it = std::back_inserter(result);
    redis.smembers("key", it);

    for (auto &e : result)
    {
        cout << e << endl;
    }
}

sismember

void test2(Redis &redis)
{
    cout << "sismember" << endl;
    redis.flushall();
    redis.sadd("key", {"111", "222", "333"});

    // 判断元素是否存在
    bool ret1 = redis.sismember("key", "111");
    bool ret2 = redis.sismember("key", "444");
    cout << "ret1: " << ret1 << endl;
    cout << "ret2: " << ret2 << endl;
}

 scard


void test3(Redis &redis)
{
    cout << "scard" << endl;
    redis.flushall();
    redis.sadd("key", {"111", "222", "333"});
    // 获取集合中元素的个数
    int ret = redis.scard("key");
    cout << "ret: " << ret << endl;
}

spop

void test4(Redis &redis)
{
    cout << "spop" << endl;
    redis.flushall();
    redis.sadd("key", {"111", "222", "333"});

    // 随机删除元素
    auto result = redis.spop("key");
    if (result)
    {
        cout << "result: " << result.value() << endl;
    }
    else
    {
        cout << "result 无效 !" << endl;
    }
}

sinter

void test5(Redis &redis)
{
    cout << "sinter" << endl;
    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);
    for (auto &e : result)
    {
        cout << e << endl;
    }
}

 

sinterstore

void test6(Redis &redis)
{
    cout << "sinterstore" << endl;
    redis.flushall();

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

    long long len = redis.sinterstore("key3", {"key1", "key2"});
    cout << "len: " << len << endl;
    set<string> result;
    auto it = std::inserter(result, result.end());
    redis.smembers("key3", it);

    for (auto &e : result)
    {
        cout << e << endl;
    }
}

求并集和求交集基本一致,不多赘述。

hash类型核心操作

hset和hget

void test1(Redis &redis)
{
    cout << "hset 和 hget" << endl;
    redis.flushall();

    // 一次添加一个元素
    redis.hset("key", "f1", "111");
    redis.hset("key", make_pair("f2", "222"));
    // 一次添加多个元素(初始化列表)
    redis.hset("key", {make_pair("f3", "333"), make_pair("f4", "444")});
    // 一次添加多个元素(迭代器)
    vector<pair<string, string>> fileds = {make_pair("f5", "555"), make_pair("f6", "666")};
    redis.hset("key", fileds.begin(), fileds.end());


    auto value = redis.hget("key", "f1");
    if (value)
    {
        cout << value.value() << endl;
    }
}

 hexists和hdel和hlen

void test2(Redis &redis)
{
    cout << "hexists hdel hlen" << endl;
    redis.flushall();

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

    bool ret1 = redis.hexists("key", "f1");
    bool ret2 = redis.hexists("key", "f3");
    cout << "ret1: " << ret1 << endl;
    cout << "ret2: " << ret2 << endl;

    // 一次删除一个filed
    long long r = redis.hdel("key", "f1");
    cout << "删除了" << r << "个" << endl;
    // 一次删除多个filed
    r = redis.hdel("key", {"f2", "f3"});
    cout << "删除了" << r << "个" << endl;

    long long len = redis.hlen("key");
    cout << "len: " << len << endl;
}

hmset和hmget

void test4(Redis &redis)
{
    cout << "hmset hmget" << endl;
    redis.flushall();

    // 初始化列表
    redis.hmset("key", {make_pair("f1", "111"), make_pair("f2", "222")});
    // 迭代器
    vector<pair<string, string>> filed = {make_pair("f3", "333"), make_pair("f4", "444")};
    redis.hmset("key", filed.begin(), filed.end());

    vector<string> result;
    auto it = std::back_inserter(result);
    redis.hmget("key", {"f1", "f2", "f3"}, it);
    for (auto &e : result)
        cout << e << endl;
}

hkeys hvals

void test3(Redis &redis)
{
    cout << "heys 和 hvals" << endl;
    redis.flushall();

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

    vector<string> fileds;
    auto itfileds = std::back_inserter(fileds);
    redis.hkeys("key", itfileds);
    for (auto &e : fileds)
        cout << e << endl;

    vector<string> values;
    auto itvalues = std::back_inserter(values);
    redis.hvals("key", itvalues);
    for (auto &e : values)
        cout << e << endl;
}

zset类型核心操作

zadd和zrange

oid test1(Redis &redis)
{
    cout << "zadd 和 zrange" << endl;
    redis.flushall();

    redis.zadd("key", "吕布", 99);
    redis.zadd("key", {make_pair("赵云", 98),
                       make_pair("关羽", 97)});
    vector<pair<string, double>> members;
    members.push_back(make_pair("张飞", 96));
    members.push_back(make_pair("马超", 95));
    redis.zadd("key", members.begin(), members.end());
    // zrange支持两种风格的查询:
    // 1.只查询member,不带score
    // 2.查询member 带 score
    // 关键在于迭代器指向的是只包含一个string,就只包含memeber
    // 迭代器指向的是一个 pair,里面包含了 string double 就查询member和score
    vector<string> memberResults;
    auto it = std::back_inserter(memberResults);
    redis.zrange("key", 0, -1, it);
    for (auto &m : memberResults)
        cout << m << endl;

    vector<pair<string, double>> membersWithScore;
    auto it2 = std::back_inserter(membersWithScore);
    redis.zrange("key", 0, -1, it2);
    for (auto &m : membersWithScore)
        cout << m.first << " " << m.second << endl;
}

zcar和zrem


void test2(Redis &redis)
{
    cout << "zcard 和 zrem" << endl;
    redis.flushall();

    redis.zadd("key", "music", 99);
    redis.zadd("key", "english", 69);
    redis.zadd("key", "math", 98);

    redis.zrem("key", "english");

    long long len = redis.zcard("key");
    cout << "len: " << len << endl;
}

zscore 

void test3(Redis &redis)
{
    cout << "zscore" << endl;
    redis.flushall();

    redis.zadd("key", "music", 99);
    redis.zadd("key", "english", 69);
    redis.zadd("key", "math", 98);

    auto score = redis.zscore("key", "music");
    if (score)
    {
        cout << "score: " << score.value() << endl;
    }
    else
    {
        cout << "score 无效!" << endl;
    }
}

zrank

void test4(Redis &redis)
{
    cout << "zrank" << endl;
    redis.flushall();

    redis.zadd("key", "music", 99);
    redis.zadd("key", "english", 69);
    redis.zadd("key", "math", 98);

    auto rank = redis.zrank("key", "music");
    if (rank)
    {
        cout << "rank: " << rank.value() << endl;
    }
    else
    {
        cout << "rank 无效!" << endl;
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

①EtherCAT转Modbus485RTU网关多路同步高速采集无需编程串口服务器

EtherCAT转Modbus485RTU网关多路同步高速采集无需编程串口服务器https://item.taobao.com/item.htm?ftt&id798036415719 型号 1路总线EC网关 MS-A2-1011 2路总线EC网关 MS-A2-1021 4路总线EC网关 MS-A2-1041 EtherCAT 串口网关 EtherCAT 转 RS485 技术规格 …

C++ Primer 交换操作

欢迎阅读我的 【CPrimer】专栏 专栏简介&#xff1a;本专栏主要面向C初学者&#xff0c;解释C的一些基本概念和基础语言特性&#xff0c;涉及C标准库的用法&#xff0c;面向对象特性&#xff0c;泛型特性高级用法。通过使用标准库中定义的抽象设施&#xff0c;使你更加适应高级…

TDengine 服务无法启动常见原因

taosd 是 TDengine 的核心服务进程&#xff0c;如果无法启动将导致整个数据库无法使用&#xff0c;了解常导致无法启动的原因&#xff0c;可以帮你快速解决问题。 1. 如何查找日志 无法启动的原因记录在日志中&#xff0c;日志文件默认在 /var/log/taos 的 taosdlog.0 或者 t…

一周学会Flask3 Python Web开发-SQLAlchemy连接Mysql数据库

锋哥原创的Flask3 Python Web开发 Flask3视频教程&#xff1a; 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili app.py下&#xff0c;我们先配置数据库连接&#xff0c;然后写一个简单sql测试。 连接配置&#xff0c;包括用户名&#xff…

【NLP 32、文本匹配任务 —— 深度学习】

大劫大难以后&#xff0c;人不该失去锐气&#xff0c;不该失去热度&#xff0c;你镇定了却依旧燃烧&#xff0c;你平静了却依旧浩荡&#xff0c;致那个从绝望中走出来的自己&#xff0c;共勉 —— 25.1.31 使用深度学习在文本匹配任务上主要有两种方式&#xff1a;① 表示型 ②…

打造智能聊天体验:前端集成 DeepSeek AI 助你快速上手

DeepSeek AI 聊天助手集成指南 先看完整效果&#xff1a; PixPin_2025-02-19_09-15-59 效果图&#xff1a; 目录 项目概述功能特点环境准备项目结构组件详解 ChatContainerChatInputMessageBubbleTypeWriter 核心代码示例使用指南常见问题 项目概述 基于 Vue 3 TypeScrip…

2008-2024年中国手机基站数据/中国移动通信基站数据

2008-2024年中国手机基站数据/中国移动通信基站数据 1、时间&#xff1a;2008-2024年 2、来源&#xff1a;OpenCelliD 3、指标&#xff1a;网络类型、网络代数、移动国家/地区、移动网络代码、区域代码、小区标识、单元标识、坐标经度、坐标纬度、覆盖范围、测量样本数、坐标…

开源订货系统哪个好 三大订货系统源码推荐

在数字化转型加速的今天&#xff0c;企业对订货系统的需求日益增长。一款优质的订货系统源码不仅能提升供应链效率&#xff0c;还能通过二次开发满足个性化业务需求。这里结合 “标准化、易扩展” 两大核心要求&#xff0c;为您精选三款主流订货系统源码&#xff0c;助您快速搭…

实现插入排序

#include <bits/stdc.h> using namespace std; int a[100005],n; void charu() {for(int i1;i<n;i){int keya[i]; //key表示要排序的数字的数值int ji-1; //j表示前面已排序好的数的个数while(j>0&&key<a[j]){a[j1]a[j]; //把a[j]往后挪j--;}a[j1]…

ROM修改进阶教程------修改安卓机型SELinux宽容的几种方式方法 以及第三方系统中如何关闭SELinux宽容

SELinux是一种强制访问控制安全机制,用于增强Linux系统的安全性。在某些情况下,可能需要对 SELinux 进行宽容设置,以满足特定的应用需求。当SELinux处于宽容模式时,系统允许违反安全策略的行为发生,但不会阻止这些行为,通常会在日志中记录这些违规事件。这与强制模式不同…

MySQL索引数据结构

目录 1 索引常用的数据结构 1.1 二叉树 1.2 平衡二叉树 1.3 红黑树 1.3 Hash表 1.4 B树 1.4 B树 2 MySQL索引的数据结构 2.1 MyISAM存储引擎索引 2.2 InnoDB存储引擎索引 2.2.1 聚集索引 2.2.2 非聚集索引 2.2.3 联合索引数 2.2.4 hash索引 1 索引常用的数据结构 1.1 二叉树 二…

分布式锁—7.Curator的分布式锁一

大纲 1.Curator的可重入锁的源码 2.Curator的非可重入锁的源码 3.Curator的可重入读写锁的源码 4.Curator的MultiLock源码 5.Curator的Semaphore源码 1.Curator的可重入锁的源码 (1)InterProcessMutex获取分布式锁 (2)InterProcessMutex的初始化 (3)InterProcessMutex.…

【高级篇】大疆Pocket 3加ENC编码器实现无线RTMP转HDMI进导播台

【高级篇】大疆Pocket 3加ENC编码器实现无线RTMP转HDMI进导播台 文章目录 准备工作连接设备RTMP概念ENCSHV2推流地址设置大疆Pocket 3直播设置总结 老铁们好&#xff01; 很久没写软文了&#xff0c;今天给大家带了一个干货&#xff0c;如上图&#xff0c;大疆Pocket 3加ENC编…

VS Code连接服务器教程

VS Code是什么 VS Code&#xff08;全称 Visual Studio Code&#xff09;是一款由微软推出的免费、开源、跨平台的代码编辑神器。VS Code 支持 所有主流操作系统&#xff0c;拥有强大的功能和灵活的扩展性。 官网&#xff1a;https://code.visualstudio.com/插件市场&#xff1…

Ubuntu-docker安装mysql

只记录执行步骤。 1 手动下载myql镜像&#xff08;拉去华为云镜像&#xff09; docker pull swr.cn-east-3.myhuaweicloud.com/library/mysql:latest配置并启动mysql 在opt下创建文件夹 命令&#xff1a;cd /opt/ 命令&#xff1a;mkdir mysql_docker 命令&#xff1a;cd m…

【MySQL】事务|概念|如何回滚|基本特性|MySQL事务隔离性具体怎么实现的

目录 1.为啥引入 2.是啥 3.如何回滚&#xff08;日志&#xff09; &#x1f525;4.面试题&#xff1a;谈谈事务的基本特性 &#xff08;1&#xff09;原子性 &#xff08;2&#xff09;一致性&#xff08;收入和支出相匹配&#xff09; &#xff08;3&#xff09;持久性…

C语言中的选择结构:决策的艺术

目录 一、选择结构的概念与意义 二、if语句 1. 基本语法 2. 示例代码 三、if-else语句 1. 基本语法 2. 示例代码 3. 嵌套if-else语句 四、switch语句 1. 基本语法 2. 示例代码 五、选择结构的注意事项 1. 条件表达式的正确性 2. if-else语句的配对问题 3. switch…

【0013】Python数据类型-列表类型详解

如果你觉得我的文章写的不错&#xff0c;请关注我哟&#xff0c;请点赞、评论&#xff0c;收藏此文章&#xff0c;谢谢&#xff01; 本文内容体系结构如下&#xff1a; Python列表&#xff0c;作为编程中的基础数据结构&#xff0c;扮演着至关重要的角色。它不仅能够存储一系…

SwanLab简明教程:从萌新到高手

目录 1. 什么是SwanLab&#xff1f; 1.1 核心特性 2. 安装SwanLab 3. 登录SwanLab账号&#xff08;云端版&#xff09; 4. 5分钟快速上手 更多案例 5. SwanLab功能组件 5.1 图表视图 5.2 表格视图 5.3 硬件监控 5.4 环境记录 5.5 组织协同 6. 训练框架集成 6.1 基…

TCP7680端口是什么服务

WAF上看到有好多tcp7680端口的访问信息 于是上网搜索了一下&#xff0c;确认TCP7680端口是Windows系统更新“传递优化”功能的服务端口&#xff0c;个人理解应该是Windows利用这个TCP7680端口&#xff0c;直接从内网已经具备更新包的主机上共享下载该升级包&#xff0c;无需从微…