初识Redis · C++客户端string

news2025/5/11 10:46:36

目录

前言:

string的API使用

set get:

expire:

NX XX:

mset,mget:

getrange setrange:

incr decr


前言:

在前文,我们已经学习了Redis的定制化客户端怎么来的,以及如何配置好Redis定制化客户端,并且简单学习了一下RESP,其实也就是了解了一下为什么Redis可以定制化客户端而已,那么,我们现在有了Redis定制化客户端的条件,我们自然就可以使用大佬们封装好的Redis的API了。

那么在本文呢,我们涉及到的是string,list和hash的多组API,因为我们是在命令行敲过的,所以学习起来也是非常快的了。

废话不多说,进入主题吧!


string的API使用

对于string的API使用,我们大致分为了 set get expire mset mget getrange setrange incr decr,大致就这么多命令,因为其他命令其实也大差不差,我们就使用过这些也都清楚了。

set get

我们创建好了redis对象之后,啥也不管,直接set,我们可以看到它的参数还是挺多的,但是当我们一看,第一个参数是key,第二个参数是val,第三个参数是chrno的时间,这不就是存活时间吗,第四个参数是一些设置,比如NX,XX等。

这样一看是非常清楚的了。

但是我们发现set的参数是Stringview类型的,这个类型和string类型来说最大的区别就是这个类型是只读的,我们可以通过阅读源码简单了解:

      begin() const noexcept
      { return this->_M_str; }

      constexpr const_iterator
      end() const noexcept
      { return this->_M_str + this->_M_len; }

      constexpr const_iterator
      cbegin() const noexcept
      { return this->_M_str; }

      constexpr const_iterator
      cend() const noexcept
      { return this->_M_str + this->_M_len; }

这里是它经过重重封装后的源码,我们可以发现它的迭代器都是const类型,也就是不允许我们修改*this了,它即只读的

因为是只读的,所以针对只读的这个操作做了很多优化,效率就比普通的std::string快了。当然了,在C++17中,标准库也提供了std::string_view,操作和string类似,只是包括了一些只读的方法。

那么我们既然set了,我们肯定要get,我们先看看get的返回值:

通过返回值和备注,我们看到了它的返回值是OptionalString,如果key是不存在的,那么会返回的就是通过nullop构造的OptionalString对象。那么OptionalString是什么呢~

实际上,它的出现是因为为了更好的处理key不存在的这个情况,在我们学习命令行的时候,我们如果使用get查看一个不存在的key,返回值是nil,也就是空的意思,那么如果它的返回值是string,我们如何表达nil

如果返回空串,我们并不排除key对应的value就是空串,如果我们再复杂一点,返回一个string*,指向的是空就代表nil,可是这样又会引发出内存安全的问题。

所以,Redis官方给出的操作是给了一个类型:OptionalString,主要是为了处理nil的情况。

但是作为C++学习者我们不能不知道boost在C++14的时候就已经引入了optional<std::string>的概念了,后来在C++17的时候我们直接就包含optional的头文件就行了。

我们先来看一段正确的使用:

void test_1(Redis& redis)
{
    redis.flushall();
    redis.set("key","111");
    auto value = redis.get("key");
    if(value)
        std::cout << value.value() << std::endl;
    else 
        std::cout << "值不存在" << std::endl;
    
}

我们一共发现了三处左右较为陌生的点,一个是我们使用auto来接收,一个是我们用if来判断value,一个是我们使用的value.value()打印。

首先,我们最能够理解的点是,<<运算符并不支持OptionalString的打印,所以我们需要通过value()方法来返回string类型,其实我们也能通过*value来解决,它们的返回值都是string,*是重载了解引用操作符,.value()就是显示的调用了对应的成员函数。

  if(value)
        std::cout << *value << std::endl;
    else 
        std::cout << "值不存在" << std::endl;
    

其次,我们还能理解的是,如果我们get的是一个不存在的键值,那么OptionalString返回值就是类似于nil的结果,所以我们最好是做一个类型检查。

最后,我们使用auto来接收,其实我们使用optional<string>接收也不是不行,但是为了方便嘛,我们可以直接使用auto接收。不过就需要你包含对应的头文件了。

对应value()方法有一个特点就是如果key不存在,就会抛异常,像这样

从上面这个代码的例子,我们认识了OptionalStringstringView

那么为了测试方便,我们最好在对Redis进行操作的时候flushall一下,以下是第一个测试的代码:

void test_1(Redis &redis)
{
    redis.flushall();
    redis.set("key", "111");
    auto value = redis.get("key");
    // std::optional<std::string> value = redis.get("key");
    // if(value)
    //     std::cout << *value << std::endl;
    // else
    //     std::cout << "值不存在" << std::endl;
    std::cout << value.value() << std::endl;
}
int main()
{
    Redis redis("tcp://127.0.0.1:6379");
    test_1(redis);
    return 0;
}

expire:

然后我们来测试以下超时时间,在set的构造函数中,我们直接加就可以了:

void test_2(Redis &redis)
{
    redis.flushall();
    redis.set("key","111",std::chrono::seconds(10));
    using namespace std::chrono_literals;
    redis.set("key1","222",10s);
    std::this_thread::sleep_for(3s);
    
    long long time = redis.ttl("key");
    std::cout << "剩余时间为:" << time << std::endl;

}

这里的代码就比较简单了,第一个点是设置过期时间有两种方式,一种是使用命令空间chrono的seconds函数或者millonseconds函数,第二种方式是我们引入命名空间chrono_literals,这样我们就可以直接使用字面值常量了。

然后我们就正常根据ttl的返回值,来接收以下time就可以了:

至于为什么使用chrono等,因为C++不像py可以直接传10表示时间,C++没有内置时间单位,所以需要一个库专门用来表示时间的,就像上面的两个。

NX XX:

接着我们来测试NX和XX:

一般默认的是ALWAYS,XX对应的是EXIST,NX对应的是NOT_EXIST:

void test_3(Redis &redis)
{
    redis.flushall();
    redis.set("key", "111", 0s,sw::redis::UpdateType::NOT_EXIST);
    auto value = redis.get("key");
    if(value)
        std::cout << value.value() << std::endl;
    else   
        std::cout << "not exist"<< std::endl;
}

mset,mget

对于mset和mget来说就是今天的难点了算是,当然仅仅相对而言,它不过是需要我们引入迭代器的概念而已。

由于传键值对的时候,get和mget的键值对都是pair类型的,所以我们使用mget的时候,官方给我们提供了两个角度,一个是使用initailizer_list,一个是使用容器的迭代器

void test_4(Redis &redis)
{
    redis.flushall();
    // redis.mset({"key1","111","key2","222"}); // error
    redis.mset({std::make_pair("key","111"),std::make_pair("key1","222")});
    std::vector<std::pair<std::string, std::string>> vec{
        std::make_pair("key2","222"),
        std::make_pair("key3","333")
    };
    std::vector<std::pair<std::string, std::string>> vec2{
        {"key4","444"},
        {"key5","555"}
    };

    redis.mset(vec.begin(), vec.end());
    redis.mset(vec2.begin(), vec2.end());

    std::vector<std::string> result;
    auto iter = std::back_inserter(result); 
    redis.mget({"key","key2","key4"},iter);
    
    for(auto ele:result)
    {
        std::cout << ele << ' ';
    }
    std::cout << std::endl;
}

对于mset和mget来说,较为特殊,使用mset的时候,我们有两种方式,一种,我们可以直接使用initailizer_list初始化,就像构建key 和 key1的时候,我们也可以通过重载迭代器的方式,即我们先定义一个容器,可以是list可以是set可以是Vector,构造好了之后,mset直接传入两个的迭代器的就可以了。

然后是mget,使用mget的时候,我们可以看看参数:

它的第二个参数是back_insert_iterator,其实就是尾插迭代器,在STL中,迭代器分为了五种类型,分别是输入迭代器,输出迭代器,前向迭代器,双向迭代器和随机访问迭代器,其中对于back_inserter来说,它就是输出迭代器的一种,mget获取到value之后,就把输出给到这个迭代器对应的容器里面。

那么给的方式,就是看位置了,比如back_inserter对应的位置是尾,那么就相当于给这个容器一直尾插。

它们对应了不同的迭代器,比如back_insert_iterator,insert_iterator,但是我们一般不会直接构造出这几个对象,我们一般会走一些辅助的函数来构造,比如back_inserter。

getrange setrange:

这两个我们如果有了C++string函数的理解,那就非常简单了,无非是我们要确定位置,给个偏移量,然后给上对应的字符串就可以了。

void test_5(Redis &redis)
{
    redis.flushall();
    redis.set("key","abcdefghijk");
    redis.setrange("key",2,"11111111");
    std::string result = redis.getrange("key",2,-1);
    std::cout << result << std::endl;
}

incr decr

同理,非常简单,我们直接给代码:

void test_6(Redis &redis)
{
    redis.flushall();
    redis.set("key", "20");
    redis.incr("key");
    auto value = redis.get("key");
    if (value)
        std::cout << value.value() << std::endl;

    redis.decr("key");

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

也是非常简单了。

其实文本篇幅较多仅是因为第一次使用Redis对应的API,那么在之后介绍list和hash的时候节奏就会快了。

本文的难点是在mset和mget的时候,如何正确使用迭代器的问题,其他难度相对来说是比较低的。


感谢阅读!

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

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

相关文章

华硕原厂系统枪神9/9p超竟版-WIN11原装开箱出厂系统安装

华硕原厂系统枪神9/9p超竟版-WIN11-24H2-专业工作站版本安装可带F12-ASUSRecovery恢复功能 适用机型&#xff1a; G635LX、G635LW、G835LX、G835LW、G615LW、G615LP、G615LM、G615LH G815LW、G815LP、G815LM、G815LH、G635LR、G835LR、G615LR、G815LR 远程恢复安装&#xff…

CF1016赛后总结

文章目录 前言T1:Ideal GeneratorT2&#xff1a;Expensive NumberT3:Simple RepetitionT4&#xff1a;Skibidi TableT5:Min Max MEXT6:Hackers and Neural NetworksT7:Shorten the Array 前言 由于最近在半期考试&#xff0c;更新稍微晚了一点&#xff0c;还望大家见谅 &#…

QT聊天项目DAY06

1.从git上同步项目 编译测试&#xff0c;编译通过 Post请求测试 测试成功 2. email is 打印有问题&#xff0c;检查 解析结果是存储在jsonResult中的&#xff0c;修改 3. 客户端实现Post验证码请求 3.1 同步Qt客户端项目 检查QT版本&#xff0c;由于我在公司用的还是QT5.12.9…

GNU,GDB,GCC,G++是什么?与其他编译器又有什么关系?

文章目录 前言1. GNU和他的工具1.1 gcc与g1.2 gdb 2.Windows的Mingw/MSVC3.LLVM的clang/clang4.Make/CMake 前言 在开始之前我们先放一段Hello World&#xff1a;hello.c #include <stdio.h>int main() {printf("Hello World");return 0; }然后就是一段老生常…

笔记整理五

STP生成树 stp生成树是用于解决二层环路问题的协议。 二层环路为有以下三种&#xff1a; 1.广播风暴 2.MAC地址的偏移&#xff08;每一次循环&#xff0c;都会导致交换机来回刷新MAC地址表记录&#xff09; 3.多帧复制 stp生成树&#xff1a;需要将原本的环型拓扑结构转换…

奥比中光tof相机开发学习笔记

针对奥比中光 tof相机&#xff0c;官方提供的资料如下ProcessOn Mindmap|思维导图 Orbbec SDK Python Wrapper基于Orbbec SDK进行设计封装&#xff0c;主要实现数据流接收&#xff0c;设备指令控制。下面就其开发适配进行如下总结&#xff1a; &#xff08;1&#xff09;系统配…

【面试向】点积与注意力机制,逐步编码理解自注意力机制

点积&#xff08;dot product&#xff09;两个向量点积的数学公式点积&#xff08;dot product&#xff09;与 Attention 注意力机制&#xff08;Attention&#xff09;注意力机制的核心思想注意力机制中的缩放点积自注意力机制中&#xff0c;谁注意谁&#xff1f; 逐步编码理解…

一个 CTO 的深度思考

今天和一些同事聊了一会&#xff0c;以下是我的观点 我的观点&#xff0c;成年人只能筛选&#xff0c;不能培养在组织中&#xff0c;应该永远向有结果的人看齐。不能当他站出来讲话的时候&#xff0c;大家还要讨论讨论&#xff0c;他虽然拿到结果了&#xff0c;但是他就是有一…

SQL通用语法和注释,SQL语句分类(DDL,DML,DQL,DCL)及案例

目录 SQL通用语法和注释 SQL语句分类&#xff08;DDL&#xff0c;DML&#xff0c;DQL&#xff0c;DCL&#xff0c;TPL&#xff0c;CCL&#xff09; DDL&#xff08;数据定义语言&#xff09; 数据库操作 查询&#xff08;SHOW、SELECT&#xff09; 创建&#xff08;CREAT…

AUTOSAR图解==>AUTOSAR_SWS_KeyManager

AUTOSAR KeyManager详细分析 AUTOSAR 4.4.0 版本密钥与证书管理模块技术分析 目录 1. 概述2. KeyManager架构 2.1 KeyManager在AUTOSAR架构中的位置2.2 架构说明 3. KeyManager模块结构 3.1 模块组件详解3.2 配置项说明 4. KeyManager证书验证流程 4.1 证书验证流程分析 5. Ke…

Jsp技术入门指南【七】JSP动作讲解

Jsp技术入门指南【七】JSP动作讲解 前言一、什么是JSP动作&#xff1f;二、核心JSP动作详解1. jsp:include&#xff1a;动态包含其他页面与<% include %>的区别 2. jsp:forward&#xff1a;请求转发到另一个页面3. jsp:param&#xff1a;为动作传递参数4. jsp:useBean&am…

10软件测试需求分析案例-查询学习信息

用户登录系统后&#xff0c;进入查询学生信息界面&#xff0c;输入查询字段值&#xff0c;点击查询按钮后&#xff0c;展示查询到的学生信息&#xff0c;可以重新输入字段值进行查询。 查询学生信息属于学生信息管理的子菜单&#xff0c;可以根据学号、姓名、性别查询。老师登录…

基于尚硅谷FreeRTOS视频笔记——6—滴答时钟—上下文切换

FreeRTOS滴答 FreeRTOS需要有一个时钟参照&#xff0c;并且这个时钟不会被轻易打断&#xff0c;所以最好选择systick 为什么需要时间参照 就是在高优先级任务进入阻塞态后&#xff0c;也可以理解为进入delay&#xff08;&#xff09;函数后&#xff0c;需要有一个时间参照&…

MCP服务,阿里云百炼,Cline,mysql-mcp-server,MCP通信原理

简介 MCP&#xff08;Model Context Protocol&#xff09;&#xff0c;模型上下文协议&#xff0c;是一种开放标准&#xff0c;用于将AI模型与外部数据源和工具建立安全的双向连接&#xff0c;它就像AI领域的USB-C接口&#xff0c;为AI模型提供了一种标准化方式来连接不同的数…

一个项目中多个Composer的使用方法

composer是依赖管理工具。 有时我们会在一个项目中使用到多个composer&#xff0c;且每个版本不同。 前提&#xff1a;例如项目xyz根目录vendor中存在阿里云的对应代码。我现在需要再composer腾讯云短信发送的SDK。 1、随便找个位置新建文件夹&#xff0c;存储腾讯云短信发送…

MCP 应用案例-网络设备批量管理

案例背景 需求痛点 企业需管理数百台跨地域网络设备&#xff08;交换机/路由器&#xff09;&#xff0c;传统方式存在&#xff1a; 人工SSH登录效率低脚本维护成本高&#xff08;不同厂商CLI语法差异&#xff09;状态监控依赖独立监控系统 解决方案 通过MCP协议构建智能网络…

国产之光DeepSeek架构理解与应用分析02

本专栏 国产之光DeepSeek架构理解与应用分析-CSDN博客 国产之光DeepSeek架构理解与应用分析02-CSDN博客 前置的一些内容理解 GPU TPU NPU的区别&#xff1f; 设计目的 GPU&#xff1a;最初是为了加速图形渲染而设计的&#xff0c;用于处理图像和视频数据&#xff0c;以提供高…

对美团leaf的初步学习

我的项目中使用的雪花算法生成的全局订单号。但是考虑到了雪花算法可能会由于时钟回拨导致生成的全局id重复。于是去研究了美团的leaf服务&#xff1a;Leaf——美团点评分布式ID生成系统 - 美团技术团队&#xff0c;并总结出该文章。 自己项目中的应用 由于对订单表做了分表&…

Nacos深度剖析与实践应用之-负载均衡

&#x1f4a1;简介 Nacos不仅提供服务注册与发现功能&#xff0c;还内置了强大的负载均衡能力。Nacos的负载均衡机制主要应用于服务消费者从服务注册中心获取服务实例列表后&#xff0c;如何选择其中一个实例进行调用的过程。 &#x1f9e0; 学习目的 这篇文章我们将探讨负载…

Photoshop安装与配置--简单攻略版

下载地址:Photoshop软件工具下载 安装完成后&#xff0c;即可运行Photoshop.exe&#xff1b;打开工具页面后&#xff0c;按照下面简单配置即可 1.编辑-》首选项-》常规 或者直接快捷键CtrlK 暂存盘&#xff1a;一定要设置为非C盘 2.性能 3.文件处理 以上配置比较基础&#xf…