Redis底层数据结构之简单动态字符串

小米手机Magisk解锁与Root完整指南:从Bootloader到模块管理 Android系统Root权限获取是设备深度定制的核心技术之一,其核心原理在于通过修改系统启动流程获取超级用户权限。Systemless Root技术通过动态挂载修改,在不触碰系统分区的前提下实现功能注入,既保证了系统完整性,又能绕过完整性检测。这一技术为设备带来了极高的可定制性,用户可以通过模块化方式增强系统功能、修改UI界面或启用隐藏特性。在小米手机生态中,由于官方提供了完整的Bootloader解锁通道,结合Magisk的无系统修改Root方案,使得安全可控的深度定制成为可能。本文聚焦于Bootloa 阅读详情

Redis底层数据结构之简单动态字符串

我们知道在C语言中常常使用空字符’\0’作为字符串的结尾标志,也就是使用N+1的字符数组来表示长度为N的字符串,Redis没有直接使用C语言中的字符串表示,而是构建了自己的一套字符串表示抽象,称为简单动态字符串SDS(simple dynamic string),至于为什么Redis不采用C语言中的字符串表示方法,这也是我们接下来要探讨的问题,我们首先给出Redis简单动态字符串的数据结构,然后说明相比C语言的字符串表示方法具有的优势

一、Redis简单动态字符串的数据结构

接下来我们来看Redis中是如何定义字符串的


    /* Redis简单动态字符串的数据结构 */
    struct sdshdr {
            //字符长度,记录buf数组中已使用的字节数量
            unsigned int len;
            //当前可用空间,记录buf数组中未使用的字节数量
            unsigned int free;
            //具体存放字符的buf
            char buf[];
    };

二、Redis采用这种结构保存字符串的原因

C语言中的字符表示,并不能满足Redis对字符串在安全性、效率以及功能方面的要求,具体体现在一下几点:

  • 获取字符串长度的时间复杂度

    在C语言中,要获取一个字符串的长度使用strlen函数,要对字符串进行遍历,其时间复杂度为O(N),而SDS本身记录了字符串的长度即len属性,所以获取一个字符串的长度的实践复杂度为O(1),特别是Redis的使用环境中存在大量、频繁的字符串操作,如果每次都调用strlen将会严重影响系统性能。

  • 缓冲区溢出

    在C语言中,我们要对一个字符串进行连接操作是,很容易造成缓冲区溢出,比如字符串char A[10] = {‘a’,’b’,’c’,’d’,’\n’},现在使用连接操作strcat(A,B),如果在进行连接操作之前未能检查A和B的长度,就会产生缓冲区溢出,这使得程序员在写程序的时候要非常小心,一不小心手滑,就是一个严重的Bug

    而在redis中,则不存在这样的问题,因为Redis保存了字符串的当前长度和可用空间,在进行连接操作的时候,会自动检查空间是否足够,不够空间系统会自动分配,程序员无需手动修改空间大小,也不会造成缓冲区溢出

  • 内存分配与释放

    在C语言中,我们对字符串进行拼接操作时,容易造成缓冲区溢出,对字符串进行缩减/截断操作时候,如果未能及时释放未使用的字节空间,又很容易造成内存泄露,因此,在Redis中如果每次对字符串的操作都涉及空间重分配,并且在分配的过程中可能涉及到系统调用,通常将是一个非常耗时的操作;

    因此在Redis中使用两种优化手段,进行优化

    (1) 空间预分配:
    当对字符串进行拼接操作时,Redis不仅分配给满足拼接操作所必要的空间,通常还会额外分配一定量的空间供下次拼接操作使用,避免每次拼接操作进行过多的内存重分配。

    (2) 分配原则:
    如果操作后的字符串长度 < 1MB ,则len的长度和free的长度一样,也就是会额外的分配一倍的空间(具体为什么这么设定还有待考究)
    如果操作后的字符串长度 >= 1MB,则Redis会分配额外的1MB未使用空间

    (3) 惰性空间释放:
    在对字符串进行缩减操作时,Redis不会立即回收缩减掉的部分空间,而是使用free字段记录下来,供下次使用,同时,Redis也提供了相应的API,可以在需要的时候释放掉这些空间,以免造成内存浪费。

三、源码实例分析

3.1 返回字符串长度的函数sdslen
    /* 计算sds的长度,返回的size_t类型的数值 */
    /* size_t,它是一个与机器相关的unsigned类型,其大小足以保证存储内存中对象的大小。 */
    static inline size_t sdslen(const sds s) {
            struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
            return sh->len;
    }

    /* 根据sdshdr中的free标记获取可用空间 */
    static inline size_t sdsavail(const sds s) {
            struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
            return sh->free;
    }

首先来看定义内联函数的好处:

因为定义为内联函数,所有要用到的地方可能不止一个文件。为了避免多个文件中定义同一个函数出错,所以放到头文件中。

看到这里大家应该还会有疑问,返回长度为什么要这样操作?我们首先看sds的定义

typedef char *sds;

sds是一个char * 类型的指针,这和我们得sdshdr有什么关系呢?我们再看sds的构造过程函数:

    /* 根据init函数指针创建字符串 */
    sds sdsnew(const char *init) {
        size_t initlen = (init == NULL) ? 0 : strlen(init);
        return sdsnewlen(init, initlen);
    }

    /* 创建新字符串方法,传入目标长度,初始化方法 */
    sds sdsnewlen(const void *init, size_t initlen) {
        struct sdshdr *sh;

        if (init) {
            /* 调用zmalloc申请size个大小的空间 */  
            sh = zmalloc(sizeof(struct sdshdr)+initlen+1);
        } else {
            //当init函数为NULL时候,调用系统函数calloc函数申请空间 */  
            sh = zcalloc(sizeof(struct sdshdr)+initlen+1);
        }
        if (sh == NULL) return NULL;
        sh->len = initlen;
        sh->free = 0;
        if (initlen && init)
            memcpy(sh->buf, init, initlen);
       //最末端同样要加‘\0’结束符
        sh->buf[initlen] = '\0';
        //最后是通过返回字符串结构体sdshdr中的buf代表新的字符串的地址
        return (char*)sh->buf;
    }

由代码可以看出sdsnewlen函数的返回值是buf的首地址,这样在看sdslen函数,通过给定的sds首地址减去sizeof(sdshdr),那么就应该是该sds所对应的sdshdr数据结构首地址,自然就能得到sh->len与sh->free。

3.2 字符串的拼接操作
/* 以t作为新添加的len长度buf的数据,实现追加操作 */
    sds sdscatlen(sds s, const void *t, size_t len) {
        struct sdshdr *sh;
        size_t curlen = sdslen(s);

        //为原字符串扩展len长度空间
        s = sdsMakeRoomFor(s,len);
        if (s == NULL) return NULL;
        sh = (void*) (s-(sizeof(struct sdshdr)));
        //多余的数据以t作初始化
        memcpy(s+curlen, t, len);
        //更改相应的len,free值
        sh->len = curlen+len;
        sh->free = sh->free-len;
        s[curlen+len] = '\0';
        return s;
    }

    /* Append the specified null termianted C string to the sds string 's'.
     *
     * After the call, the passed sds string is no longer valid and all the
     * references must be substituted with the new pointer returned by the call. */
    /* 追加t字符串 */
    sds sdscat(sds s, const char *t) {
        return sdscatlen(s, t, strlen(t));
    }
参考书籍:

黄健宏的《Redis设计与实现》

参考博客:

http://blog.youkuaiyun.com/androidlushangderen/article/

http://blog.youkuaiyun.com/xiejingfa/article

2022-2028年全球与中国玄武岩连续纤维(BCF)行业深度分析 本文研究全球与中国市场玄武岩连续纤维(BCF)的发展现状及未来发展趋势,分别从生产和消费的角度分析玄武岩连续纤维(BCF)的主要生产地区、主要消费地区以及主要的生产商。重点分析全球与中国市场的主要厂商产品特点、产品规格、不同规格产品的价格、产量、产值及全球和中国市场主要生产商的市场份额。 主要生产商包括: Kamenny Vek TESPE Mafic Technobasalt Russian Basalt ISOMATEX SA Sudaglass Basalt Fiber INNEGRA LAVAint 阅读详情

相关推荐

Jev模型入门:从API接入到本地部署的完整实践指南

大模型的应用方式正从单一聊天窗口转向复杂的自动化工作流,而推理模型因具备指令跟随与结构化输出能力,成为构建Agent和数据处理管线的关键组件。Jev作为面向通用场景的推理模型,不仅提供了OpenAI兼容的API接口,还支持本地量化部署,兼顾隐私、可控性与长期成本。本文从模型定位出发,明确Jev与基座模型的差异,随后介绍密钥管理、API调用、参数调优,并深入演示基于Ollama与llama.cpp的本地部署流程,包括GGUF模型文件与chat template的匹配要点。针对实践中的常见问题,如密钥泄露、ma

weixin_29057237的博客 357

redis:redis的底层数据结构

一提到redis,我们就会想到“快"。那redis的快到底是快在哪里呢?实际上,这里有一个重要的表现:它接收到一个键值对操作后,能以微秒级别的速度找到数据,并快速完成操作。 为什么redis能有这么突出的表现呢? 一方面,这是因为它是内存数据库,所有操作都在内存上完成,内存的访问速度本身就很快 另一方面,这要归功于它的数据结构。这是因为,键值对是按照一定的数据结构来组织的,操作键值对最终就是对数据结构进行增删改查操作,所以高效的数据结构是redis快速处理的基础。 redis中的数据结构有两种意思:

OceanStar的博客 1万+

同济大学C++mooc第五、六讲参考答案

同济大学C++moooc第5、6讲 第五讲 第一题 随机产生10个30~100(包括30,100)的正整数,求它们的最大值、最小值、平均值,并显示整个数组的值和结果。 代码片. // #include<iostream> const int n = 10; using namespace std; int main() { int s[n],i,j,t,sum=0; for (i = 0; i < n; i++)

cjzui6666的博客 1万+

Redis底层数据结构(详细篇)

redis主要的底层数据结构

kingandsong的博客 4887

二阶高通滤波器硬件设计:5种RC到有源方案对比与选型

在传感器前端和音频信号调理中,二阶高通滤波器是抑制直流偏移与低频干扰的关键模块。其设计核心围绕截止频率、品质因数Q、增益与噪声四个指标展开,其中Q值决定滤波器在转折频率附近的响应形状,如Butterworth最大平坦响应需Q=0.707。从纯无源RC电路到引入运放的有源拓扑,演变逻辑在于解决负载效应、提供增益并借助反馈提升Q值。实际工程中,RC级联、VCVS(Sallen-Key)、状态变量及回转器模拟电感等方案各有适用场景:RC电路简单但Q值受限,Sallen-Key单运放即可实现高Q,状态变量适合Q值独

weixin_32770423的博客 99

别再纠结Dify了!实测对比RAGFlow和Dify,聊聊企业知识库到底该怎么选

本文深度对比了RAGFlow和Dify在企业知识库选型中的实际表现,从技术架构、性能指标、部署成本等多维度进行分析。RAGFlow在复杂文档处理和准确率方面表现突出,适合有专职技术团队的企业;而Dify则以低门槛和快速部署见长。文章提供了六维度评估法,帮助企业根据文档复杂度、准确率要求等技术指标做出明智选择。

weixin_30897233的博客 669

Redis底层数据结构(图文详解)

redis底层数据结构图文详解,图片配合文字更好的理解redis的底层数据结构

Me_xuan的博客 1万+

相机图像质量研究(15)常见问题总结:光学结构对成像的影响--暗角

老式相机的暗角往往比较明显,周围一圈能够明显感觉到亮度下降的很多,当然有些电源模仿手持相机的摄影效果,也能看到周围明显的亮度下降。一、

xiaoshixiu的专栏 1123

Redis的六种底层数据结构

Redis中5种数据类型的底层数据结构Redis的五大数据类型1、Redis 键(key)2、Redis字符串(String)3、Redis列表(List)4、Redis集合(Set)5、Redis哈希(Hash)6、Redis有序集合Zset(sorted set) Redis的五大数据类型 redis常见数据类型操作命令:http://redisdoc.com/ 简介 1、Redis 键(key) 2、Redis字符串(String) 3、Redis列表(List) 4、Redis集合(Set) 5

小小本科生 4020

VCS多lib编译实战:如何高效处理Verilog模块重命名与分开编译

本文深入探讨了在复杂SoC设计中,如何利用VCS工具实现多库编译,以高效处理Verilog模块重命名与分开编译的工程难题。通过解析synopsys_sim.setup配置、Config映射机制,并提供完整的自动化脚本实战指南,帮助工程师构建健壮、可维护的编译环境,从而显著提升大型项目的编译效率和代码管理灵活性。

weixin_29169505的博客 357

Windows打印体系结构之Windows内置打印驱动程序

时间是一种很残酷的东西,它只会冲淡能够冲淡的,但也会洗尽铅华帮你留下该留下的。1.3.4、Windows内置打印驱动程序现在的Windows下的打印机驱动程序通常分为V4打印机驱动程序(V4 Printer Driver)、XPS打印机驱动程序(XPSDrv)、微软通用打印机驱动程序(Microsoft UniversalPrinter Driver)、微软PostScri...

weixin_34309435的博客 2253

Redis底层数据结构

Redis 是一个数据结构服务器。Redis 的核心是提供一组原生数据类型,可帮助您解决各种各样的问题,从缓存到队列再到事件处理。下面是每种数据类型的简短描述,以及更广泛的概述。

War_wick的博客 1915

Claude Code 分布式并行开发最佳实践:1中枢+10Worker跨多Git仓库全流程落地

本文介绍了一套基于Claude Code和Git Worktree的分布式并行开发方案,解决多仓库、微服务开发中的核心痛点。方案采用"1个决策中枢+10个并行Worker"架构,中枢负责任务拆解和调度,每个Worker绑定独立仓库实现完全隔离开发。关键优势包括:任务并行效率提升10倍、跨仓库依赖自动编排、版本统一管理、零第三方依赖。文章详细提供了环境准备、架构设计、中枢搭建和初始化脚本等全流程落地指南,特别适合多仓库协同的全栈开发场景。

AI天才研究院 243

Redis系列 - Redis底层数据结构(简单动态字符串(SDS)、链表、字典、跳跃表、整数集合、压缩列表)

Redis系列 - Redis基本数据类型

u011485472的博客 2133

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

redis底层数据结构——简单动态字符串

redis简单动态字符串 redis SDS

SO_zxn的博客 1249

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

Redis底层数据结构-简单动态字符串SDS

Redis 只会使用 C 字符串作为字面量, 在大多数情况下, Redis 使用 SDS (Simple Dynamic String,简单动态字符串)作为字符串表示。比起 C 字符串, SDS 具有以下优点:常数复杂度获取字符串长度。杜绝缓冲区溢出。减少修改字符串长度时所需的内存重分配次数。二进制安全。兼容部分 C 字符串函数。

Sajor的博客 1304

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Redis学习笔记(五)--Redis底层数据结构之简单动态字符串SDS

本文介绍了Redis底层的数据结构简单动态字符串SDS的原理,为作者在学习Rdis时的笔记,特此比记录,以便后续学习中能够查漏补缺。

Scr1Pt的博客 1412

源码解析Redis底层数据结构——简单动态字符串

Redis底层数据结构-简单动态字符串 源码 source/redis/src/sds.h 定义 简单动态字符串(SDS,simple dynamie String),Redis底层是C编写的,但是Redis并没有直接使用C语言的字符串类型,而是自己创建了一套新的字符串类型,下面为SDS源码定义: 每个 sds.h/sdshdr 结构表示一个 SDS 值: struct sdshdr { // redis 3.0 int len; // 记录 buf 数组中已使用字节的数量,等于 SDS 所

m0_54369189的博客 310

Redis底层数据结构之SDS简单动态字符串

在Redis的数据库里面,包含字符串值的键值对咋底层都是由SDS实现的,也就是简单动态字符串(simple dynamic string)。 除了用来保存数据库中的字符串值之外,SDS还被用作缓存区(buffer)。 1.SDS的定义 每个sds.h/sdshdr结构表示一个SDS的值: struct sdshdr{ //记录buf数组中已使用字节的数量 //等于SDS所保存字符串的长度 int len; //记录buf数组中未使用字节的数量 int free; //字节数组,用于保存字符串

d303577562的博客 225

Redis 数据结构字符串底层剖析(简单动态字符串)

引言 Redis没有直接使用C语言传统的字符串表示,而是自己构建了一种名为简单动态字符串(simple dynamic string,SDS)的抽象类型,并将SDS用作Redis的默认字符串表示. 在Redis 里面,C字符串只会作为字符串面量(string literal)用在一些无须对字符串值进行修改的地方。 在一个可以被修改的字符串值里面,Redis就会使用SDS来表示字符串值,比如:Redis数据库里面,包含字符串值得健值对在底层得实现都是由SDS实现的以及AOF的缓冲区等(注:在一些无需更改

wangxuelei036的博客 1万+

Redis 底层数据结构 —— SDS(简单动态字符串)

sds结构详解

m0_62909831的博客 2247

Redis数据结构底层实现---简单动态字符串

简介 在Redis中STRING类型数据结构使用了简单动态字符串(simpple dynamic string, SDS)去实现。Redis没有直接使用C语言中的字符串,而是采用SDS去保存字符串的值,主要原因有一下几点。 C语言中获取一个字符串长度时间复杂度为O(n),而在SDS中这一操作时间复杂度仅为O(1) 在C语言中容易出现缓冲区溢出的问题,由于没有记录字符串长度,在合并两个字符串时可能出现空间不足而使得合并后字符串的内容被覆盖 SDS将连续增⻓N次字符串所需的内存????分配次数从必定 N次降低

m0_46882216的博客 192

常用链表内置方法 C语言,【redis】redis底层数据结构原理--简单动态字符串 链表 字典 跳跃表 整数集合 压缩列表等...

Redis有五种数据类型string、list、hash、set、zset(字符串、哈希、列表、集合、有序集合)并且自实现了简单动态字符串、双端链表、字典、压缩列表(ziplist)、整数集合、跳跃表(skiplist)等数据结构。Redis底层使用了多种数据结构来实现各种特性。对于Redis底层实现的了解,可以让我们对Redis工作原理更加清晰。Redis的五种数据类型Redis数据结构与内部编...

weixin_39845306的博客 241

Redis-底层数据结构-简单动态字符串

简单动态字符串(SDS) redis是C语言实现的,但redis中的字符串并没有直接用C语言中的字符串表示,而是自己构建了一种简单的动态字符串类型(SDS)。 在redis里面,C字符串只用作字面量,用在一些不会修改的地方,eg:打日志。 SDS的结构 struct sdshdr { // 记录buf数组已经使用的字节数量 // 等于SDS所保存字符串的长度 int len; // 记录buf数组未经使用的字节数量 int free; // 字节数据,用于保存字符串 char buf[

shiwentong的博客 183

Redis系列(一)底层数据结构之简单动态字符串

前言 Redis 已经是大家耳熟能详的东西了,日常工作也都在使用,面试中也是高频的会涉及到,那么我们对它究竟了解有多深刻呢? 我读了几本 Redis 相关的书籍,尝试去了解它的具体实现,将一些底层的数据结构及实现原理记录下来。 本文将介绍 Redis 中最基础的 字符串 的实现方法。 它是Redis的字符串键的主要实现方法. 定义 Redis 是使用 C 语言实现的,但是 Redis 中...

求关注 422

Redis的底层数据结构----简单动态字符串_SDS

Redis的底层数据结构一. 简单动态字符串(SDS)①. SDS 定义②. SDS优点①、常数复杂度获取字符串长度②、杜绝缓冲区溢出③、减少修改字符串的内存重新分配次数④、二进制安全⑤、兼容部分 C 字符串函数二. 链表①. 链表定义②. Redis链表特性③. 双向无环链表在Redis中的使用 一. 简单动态字符串(SDS) Redis 是用 C 语言写的,但是对于Redis的字符串,却不是 C 语言中的字符串(即以空字符’\0’结尾的字符数组),它是自己构建了一种名为 简单动态字符串(simple

407
上一篇: Androguard的环境搭建
下一篇: Redis底层数据结构之链表
code&poetry
博客等级 码龄14年 134粉丝 34原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值