Redis之SDS数据结构的使用

news2024/11/17 11:44:13

目录

  • 序言
  • 字符串
    • char*字符串数组
  • 简单动态字符串SDS

序言

Redis的几种基本数据结构有字符串(String)、哈希(Hash)、列表(List)、集合(Set)、有序集合(Sorted Set),这些是最常见的,也能在官网上查看到。

官网链接:Redis 教程_redis教程

字符串

前面也提到过字符串是设计了简单动态字符串SDS(Simple Dynamic String)结构来表示字符串。这种数据结构可以提升字符串的操作效率,并可以保存二进制数据。

先思考一个问题:

Redis是用C语言实现的,那么为什么没有复用C语言的字符串实现方法,而选用了SDS呢?

char*字符串数组

C语言实现字符串使用的是char*字符串数组,它是一块连续的内存空间,一次存放了字符串的每一个字符,并且最后一个字符是“\0”,用来标识字符串的结尾位置,如下图,

连续的内存空间的所有字符串没有分隔符计算机就没办法区分字符串与字符串之间的位置。在C语言标准库中字符串的操作函数就会通过检查字符串数组中是否有“\0”来判断字符串是否结束。例如字符串操作函数strlen函数,它就是在遍历字符串数组中的每一个字符,并进行计数,直到检查到“\0”,它的时间复杂度是O(n)。流程如下,

简单动态字符串SDS

SDS的数据结构里包含:字符串实际长度,字符串分配空间长度,SDS类型,字符数组,其中字符数组buf[]用来保存实际数据,如下图,

再来看看类似的字符操作函数sdslen函数的源码(在sds.h文件中),直接根据SDS类型返回对应的字符串现有长度,避免了对字符串的遍历,时间复杂度变成了O(1),当然也会付出一点代价增加了空间复杂度。这都是设计人员让数据操作更加高效。源码如下,

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

static inline size_t sdslen(const sds s) {

    unsigned char flags = s[-1];

    switch(flags&SDS_TYPE_MASK) {

        case SDS_TYPE_5:

            return SDS_TYPE_5_LEN(flags);

        case SDS_TYPE_8:

            return SDS_HDR(8,s)->len;

        case SDS_TYPE_16:

            return SDS_HDR(16,s)->len;

        case SDS_TYPE_32:

            return SDS_HDR(32,s)->len;

        case SDS_TYPE_64:

            return SDS_HDR(64,s)->len;

    }

    return 0;

}

再来看一下字符串的拷贝源码,操作都使用了字符串的现有长度,拷贝后进行更新。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

sds sdscpylen(sds s, const char *t, size_t len) {

    // 判断字符串数组分配的空间长度是不是小于字符串数组当前长度

    if (sdsalloc(s) < len) {

        // 根据要追加的长度len-sdslen(s)和现有长度,判断是否增加新的空间

        s = sdsMakeRoomFor(s,len-sdslen(s));

        if (s == NULL) return NULL;

    }

    // 将源字符串t中len长度的数据拷贝到目标字符串结尾

    memcpy(s, t, len);

    // 拷贝完后,在目标字符串结尾加上\0

    s[len] = '\0';

    // 设置字符串数组最新当前长度

    sdssetlen(s, len);

    return s;

}

SDS把目标字符串的空间检查和扩容封装在了sdsMakeRoomFor函数中,追加、打印、复制等操作都会调用该函数。可以看到该函数根据sds的信息进行动态扩容,源码如下,

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

sds sdsMakeRoomFor(sds s, size_t addlen) {

    void *sh, *newsh;

    // 获取sds可用空间

    size_t avail = sdsavail(s);

    size_t len, newlen;

    char type, oldtype = s[-1] & SDS_TYPE_MASK;

    int hdrlen;

  

    // 如果可用空间大于等于要增加的空间,则直接返回

    if (avail >= addlen) return s;

    // sds长度

    len = sdslen(s);

    // sds指针

    sh = (char*)s-sdsHdrSize(oldtype);

    // 新字符串长度

    newlen = (len+addlen);

    // 如果新长度小于最大预分配长度,则进行两倍扩容

    if (newlen < SDS_MAX_PREALLOC)

        newlen *= 2;

    else

        newlen += SDS_MAX_PREALLOC;

    type = sdsReqType(newlen);

    // SDS类型5转换为类型8

    if (type == SDS_TYPE_5) type = SDS_TYPE_8;

  

    hdrlen = sdsHdrSize(type);

    if (oldtype==type) {

        newsh = s_realloc(sh, hdrlen+newlen+1);

        if (newsh == NULL) return NULL;

        s = (char*)newsh+hdrlen;

    } else {

        /* Since the header size changes, need to move the string forward,

         * and can't use realloc */

        newsh = s_malloc(hdrlen+newlen+1);

        if (newsh == NULL) return NULL;

        memcpy((char*)newsh+hdrlen, s, len+1);

        s_free(sh);

        s = (char*)newsh+hdrlen;

        s[-1] = type;

        sdssetlen(s, len);

    }

    sdssetalloc(s, newlen);

    return s;

}

 可以看到sdsMakeRoomFor函数中sdshdr5类型不再使用直接转换成了sdshdr8类型,它们是SDS设计的5种类型,分别表示sdshdr5sdshdr8sdshdr16sdshdr32sdshdr64,下面就看一下这几种类型的结构源码,如下图,

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

struct __attribute__ ((__packed__)) sdshdr5 {

    unsigned char flags; /* 3 lsb of type, and 5 msb of string length */

    char buf[];

};

struct __attribute__ ((__packed__)) sdshdr8 {

    uint8_t len; /* used */

    uint8_t alloc; /* excluding the header and null terminator */

    unsigned char flags; /* 3 lsb of type, 5 unused bits */

    char buf[];

};

struct __attribute__ ((__packed__)) sdshdr16 {

    uint16_t len; /* used */

    uint16_t alloc; /* excluding the header and null terminator */

    unsigned char flags; /* 3 lsb of type, 5 unused bits */

    char buf[];

};

struct __attribute__ ((__packed__)) sdshdr32 {

    uint32_t len; /* used */

    uint32_t alloc; /* excluding the header and null terminator */

    unsigned char flags; /* 3 lsb of type, 5 unused bits */

    char buf[];

};

struct __attribute__ ((__packed__)) sdshdr64 {

    uint64_t len; /* used */

    uint64_t alloc; /* excluding the header and null terminator */

    unsigned char flags; /* 3 lsb of type, 5 unused bits */

    char buf[];

};

sdshdr5已不再使用,所以在函数中做了处理,把sdshdr5类型转换为sdshdr8类型。前面也提到过SDS是紧凑型字符串数据结构,以sdshdr8为例,它是用的是uint8_t即8位无符号整型,会占用1字节的内存空间。SDS之所以设计不同的结构是为了能灵活保存不同大小的字符串,从而有效节省内存空间。

另外,__attribute__ ((__packed__))标志可以告诉编译器在编译以上数据结构时,不实用字节对齐的方式(不满8字节的整数倍,则会自动补齐),而是采用紧凑的方式分配内存。

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

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

相关文章

美国原装二手KEYSIGHT N8975A是德N8975B噪声系数分析仪

KEYSIGHT / AGILENT N8975A N8975B 噪声系数分析仪 Keysight N8975B 高性能噪声系数分析仪旨在进行快速、准确和可重复的噪声系数测量。它有一个多点触控界面&#xff0c;允许拉伸、捏合和拖动手势。大多数菜单都可以通过手指轻点来访问&#xff0c;而无需切换屏幕。通过允许…

【数据结构】第10周

目录 1.图的基本术语 2.图的存储 2.1邻接矩阵 2.2.邻接表 3.图的遍历 3.1 深度搜索 dfs 3.2 广度搜索 bfs 4.图的应用 4.1 最小生成树 4.1.1 普里姆算法 4.1.2 克鲁斯卡尔算法 4.2 最短路径 4.2.1 Dijkstra算法 4.2.2 Floyd算法 4.3 拓扑排序 4.4 关键路径 一些…

chatgpt赋能python:Python创建一个Animal类介绍

Python创建一个Animal类介绍 Python是一种高级编程语言&#xff0c;其简单易学、灵活性强、可读性高以及强大的库使得Python非常受欢迎。在Python中创建类非常容易且非常常见&#xff0c;我们可以使用Python创建各种类型的类。今天&#xff0c;我们将讨论如何使用Python创建一…

OpenAI发布最新研究让大模型数学推理直接达到SOTA

&#x1f989; AI新闻 &#x1f680; OpenAI发布最新研究&#xff1a;基于过程奖励的监督方法&#xff0c;让大模型数学推理直接达到SOTA 摘要&#xff1a;OpenAI最新研究基于GPT-4微调&#xff0c;采用过程监督和结果监督两种监督方法&#xff0c;奖励每个正确推理步骤的过程…

微信小程序开发实战 ⑨(TabBar)

作者 : SYFStrive 博客首页 : HomePage &#x1f4dc;&#xff1a; 微信小程序 &#x1f4cc;&#xff1a;个人社区&#xff08;欢迎大佬们加入&#xff09; &#x1f449;&#xff1a;社区链接&#x1f517; &#x1f4cc;&#xff1a;觉得文章不错可以点点关注 &#x1f4…

机器学习:基于XGBoost对信用卡欺诈行为的识别

机器学习&#xff1a;基于XGBoost对信用卡欺诈行为的识别 作者&#xff1a;i阿极 作者简介&#xff1a;Python领域新星作者、多项比赛获奖者&#xff1a;博主个人首页 &#x1f60a;&#x1f60a;&#x1f60a;如果觉得文章不错或能帮助到你学习&#xff0c;可以点赞&#x1f4…

Kafka集群安装部署

Kafka集群安装部署 简介 Kafka是一款分布式的、去中心化的、高吞吐低延迟、订阅模式的消息队列系统。 同RabbitMQ一样&#xff0c;Kafka也是消息队列。不过RabbitMQ多用于后端系统&#xff0c;因其更加专注于消息的延迟和容错。 Kafka多用于大数据体系&#xff0c;因其更加…

大数据Doris(三十三):Spark集群搭建

文章目录 Spark集群搭建 一、Spark Standalone 集群搭建 1、下载Spark安装包 2、上传、

Vue+springboot老年人活动报名教育学习系统

基于java语言设计并实现了老年教育学习系统。该系统基于B/S即所谓浏览器/服务器模式&#xff0c;应用Springboot框架&#xff0c;选择MySQL作为后台数据库。系统主要包括首页、个人中心、老年人管理、负责人管理、课程教师管理、组织者管理、活动信息管理、活动申请管理、活动策…

python实现Canny算子边缘检测算法

边缘检测是一种将图片中关键信息表现出来的一种图片技术&#xff0c;它的结果并不是字面意思上的获取图片边缘&#xff0c;而是将图片有用的信息勾勒出来&#xff0c;类似素描的结果&#xff0c;但是已经去掉了很多信息。如下所示&#xff0c;一张原始的图片是这样的&#xff1…

HGFormer:用于领域广义语义分割的层级式分组Transformer

文章目录 HGFormer: Hierarchical Grouping Transformer for Domain Generalized Semantic Segmentation摘要本文方法实验结果 HGFormer: Hierarchical Grouping Transformer for Domain Generalized Semantic Segmentation 摘要 目前的语义分割模型在独立同分布条件下取得了…

正准备升职加薪?自动化测试框架设计原则必须要知道...

本期小编将为大家带来自动化测试方面的基础知识&#xff0c;正在学习及将要提升学习自动化的同学们可自行参考啦&#xff0c;希望大家都可以尽快升职加薪&#xff01; 下面跟着小编来认识一下自动化测试框架及其原则吧&#xff01; 自动化测试框架 自动化测试框架需要基于特…

Transfomer编码器中自注意力机制、前馈网络层、叠加和归一组件等讲解(图文解释)

Transformer中的编码器不止一个&#xff0c;而是由一组N个编码器串联而成&#xff0c;一个编码的输出作为下一个编码器的输入&#xff0c;如下图所示&#xff0c;每一个编码器都从下方接收数据&#xff0c;再输出给上方&#xff0c;以此类推&#xff0c;原句中的特征会由最后一…

Nginx网络服务——主配置文件-nginx.conf

Nginx网络服务——主配置文件-nginx.conf 一、全局配置的六个模块简介二、nginx配置文件的详解1.全局配置模块2.I/O 事件配置3.HTTP 配置4.Web 服务的监听配置5.其他设置 三、访问状态统计与控制1.访问状态统计2.基于授权的访问控制3.基于客户端的访问控制 一、全局配置的六个模…

6个音效素材库,自媒体必备~

视频剪辑、自媒体必备的6个音效素材网站&#xff0c;再也不用担心找不到声音素材了&#xff0c;赶紧收藏起来&#xff0c;一定对你非常有用~ 菜鸟图库 https://www.sucai999.com/audio.html?vNTYxMjky ​ 菜鸟图库是一个综合性素材网站&#xff0c;站内涵盖设计、图片、办公、…

Termius使用[分屏同时操作]

免费版可用ssh&#xff0c;tftp连接服务器&#xff0c;界面美观操作简洁 一 安装 官网 进入官网&#xff0c;登陆后进入个人页面&#xff0c;下载对应系统软件 安装完成后&#xff0c;在应用中登陆账号后即可开始使用 二 基本操作 [只针对ssh连接服务器操作] 2.1 连接服务…

MySQL_11 用户管理和权限管理

目录 一、用户管理 1.用户介绍 : 2.操作指令 : 3.代码演示 : 二、权限管理 1.MySQL常见权限汇总 &#xff1a; 2.相关指令 : 3.代码演示 : 一、用户管理 1.用户介绍 : MySQL中的用户都存储在系统数据库mysql中的user表中&#xff0c;如下图所示 : user表中&#xff0c…

银行从业——法律法规——金融基础知识

第二章 金融基础知识 第二节 货币政策 【 知识点1】 货币政策目标 制定和实施货币政策&#xff0c; 首先必须明确货币政策最终要达到的目的&#xff0c; 即货币政策的最终目标。中央银行通过货币政策工具操作直接引起操作目标的变动&#xff0c;操作目标的变动又通过一定的途…

深度:解密数据之力,奏响制造业智能升级的狂想曲!

‍数据智能产业创新服务媒体 ——聚焦数智 改变商业 在21世纪的今天&#xff0c;我们正在经历着一个伟大的变革&#xff0c;一个由数字技术引领的产业革命——智能制造。在这场变革中&#xff0c;大数据、人工智能、5G专网、工业物联网和智能机器人等尖端技术&#xff0c;正如…

Grounded Language-Image Pre-training(论文翻译)

文章目录 Grounded Language-Image Pre-training摘要1.介绍2.相关工作3.方法3.1统一构建3.2.语言感知深度融合3.3.使用可扩展的语义丰富数据进行预训练 4.迁移到既定的基准4.1.COCO上的zero-shot和监督迁移学习4.2.LVIS上的zero-shot 迁移学习4.3.Flickr30K实体上的 phrase gro…