01、字符传实现为什么是SDS而不是char*?
问题:
1. sds 是什么 ?
2. sds 相对于char * 有什么好处 ?解决了哪些疑难杂症?
3. sds 有什么不足?可以优化的点?
思考下:
平常工作开发中,我们记录一条用户信息、订单信息,redis内部是怎么帮我们把数据存起来的呢,是随意allot 一个内存,放进去嘛 ?
键值对中的键是字符串,值有时也是字符串。我们在 Redis 中写入一条用户信息,记录了用户姓名、性别、所在城市等,这些都是字符串,如下所示:
SET user:id:100 {"name": "zhangsan", "gender": "M","city":"beijing"}
大家如果有实际使用过redis那么都应该知道,类似这种字符串的操作在Redis中其实是最常见的,那么既然它被使用这么频繁,字符串的存储需要满足什么要求吗 ?
- 能支持丰富且高效的字符串操作,比如字符串追加、拷贝、比较、获取长度等;
- 能保存任意的二进制数据,比如图片等 能尽可能地节省内存开销。
在C语言中,使用char来实现字符串存储,定义了很多方法来满足日常开发,比如字符串比较函数 strcmp、字符串长度计算函数 strlen、字符串追加函数 strcat 等。那么下面来看下为什么不能直接使用Char
为什么Redis 不使用C的char呢 ?
首先redis是一个纯内存操作,结合它的使用属性,可以想到redis 要求性能高、存储小等特点,那么反过来看下char是否可以满足呢?
首先以字符串‘redis’为例看下使用char的存储结构,一块连续的内存空间,依次存放了字符串中的每一个字符数组结构。

\0 ,表示一个字符串的结尾,在C语言中,char* 指针只是指向字符数组的起始位置,而字符数组的结尾位置就用”\0"表示
如strlen 函数流程:
通过一段代码,可以更清晰看到\0的作用,这里我创建了两个字符串变量 a 和 b,分别给它们赋值为"red\0is"和"redis\0”。然后,我用 strlen 函数计算这两个字符串长度,如下所示:
include <stdio.h>include <string.h>int main(){char *a = "red\0is";char *b = "redis\0";printf("%lu\n", strlen(a));printf("%lu\n", strlen(b));return 0;}
代码执行后,输出结果分别是3和5,a =3 , b= 5 字节。那么看到这问题也就出来了,我们都知道Redis是有保存二进制数据的需求的。如果使用char来存储会使二进制数据被截断,完全支持不了。
另外,回过来看strlen函数,虽然可以计算字符串长度,但是它也会带来另一方面的负面影响,也就是会导致操作函数的复杂度增加。需要遍历字符数组中的每一个字符,才能得到字符串长度,所以这个操作函数的复杂度是 O(N)。
再来看另一个函数,也是常用的strcat追加功能。strcat 函数是将一个源字符串 src 追加到一个目标字符串的末尾。该函数的代码如下所示:
char *strcat(char *dest, const char *src) {//将目标字符串复制给tmp变量char *tmp = dest;//用一个while循环遍历目标字符串,直到遇到"\0"跳出循环,指向目标字符串的末尾while(*dest)dest++;//将源字符串中的每个字符逐一赋值到目标字符串中,直到遇到结束字符while((*dest++ = *src++) != '\0' )return tmp;}
从代码中可以看到,strcat 函数和 strlen 函数类似,复杂度都很高,也都需要先通过遍历字符串才能得到目标字符串的末尾。对于 strcat 函数来说,还要再遍历源字符串才能完成追加。另外,它在把源字符串追加到目标字符串末尾时,还需要确认是否空间足够。
SDS的设计
SDS(即简单动态字符串)的数据结构。下面我们一起来看看SDS 结构设计

SDS 结构里包含了一个字符数组 buf[],用来保存实际数据。同时,SDS 结构里还包含了三个元数据,分别是字符数组现有长度 len 、分配给字符数组的空间长度 alloc ,以及 SDS 类型 flags。
typedef char *sds;
Redis 源码中 SDS 的定义,Redis 使用 typedef 给 char* 类型定义了一个别名,这个别名就是 sds。如上。那么到这里也可看得出本质其实还是个字符数组,只是在字符数组基础上增加了额外的元数据。
接下来,看下sdsnewlen 函数:
sds sdsnewlen(const void *init, size_t initlen) {void *sh; //指向SDS结构体的指针sds s; //sds类型变量,即char*字符数组...sh = s_malloc(hdrlen+initlen+1); //新建SDS结构,并分配内存空间...s = (char*)sh+hdrlen; //sds类型变量指向SDS结构体中的buf数组,sh指向SDS结构体起始位置,hdrlen是SDS结构体中元数据的长度...if (initlen && init)memcpy(s, init, initlen); //将要传入的字符串拷贝给sds变量ss[initlen] = '\0'; //变量s末尾增加\0,表示字符串结束return s;
详细说下,创建一个SDS的过程:sdsnewlen 函数会新建 sds 类型变量(也就是 char* 类型变量),并新建 SDS 结构体,把 SDS 结构体中的数组 buf[] 赋给 sds 类型变量。最后,sdsnewlen 函数会把要创建的字符串拷贝给 sds 变量。
到此,我们已经了解SDS底层数据结构,那么对比一下与传统的C语言操作字符串,SDS有哪些优点:SDS 结构中记录了字符数组已占用的空间和被分配的空间,提高读写效率。同样以字符串追加为例,Redis 中实现字符串追加的函数是 sds.c 文件中的 sdscatlen 函数。
sds sdscatlen(sds s, const void *t, size_t len) {//获取目标字符串s的当前长度size_t curlen = sdslen(s);//根据要追加的长度len和目标字符串s的现有长度,判断是否要增加新的空间s = sdsMakeRoomFor(s,len);if (s == NULL) return NULL;//将源字符串t中len长度的数据拷贝到目标字符串结尾memcpy(s+curlen, t, len);//设置目标字符串的最新长度:拷贝前长度curlen加上拷贝长度sdssetlen(s, curlen+len);//拷贝后,在目标字符串结尾加上\0s[curlen+len] = '\0';return s;
}
同样,我们来画个流程图来梳理一下sdscatlen执行流程:

和char操作相比,SDS 通过记录字符数组的使用长度和分配空间大小,避免了对字符串的遍历操作,降低了操作开销,进一步就可以帮助诸多字符串操作更加高效地完成.
紧凑型字符串结构
简单点说,sds设计了不同的结构头(也就是不同的类型),为了能灵活的保存不同大小的字符串,从而有效节省内存空间。保存不同大小的字符串时,结构头占用的内存空间也不一样,在保存小字符串时,结构头占用的空间也比较少。
官方话语来说就是:SDS 一共设计了 5 种类型,分别是 sdshdr5、sdshdr8、sdshdr16、sdshdr32 和 sdshdr64。主要区别就在于,它们数据结构中的字符数组现有长度 len 和分配空间长度 alloc,这两个元数据的数据类型不同。
// attribute__ (__packed__) 采用紧凑的方式分配内存
struct __attribute__ ((__packed__)) sdshdr8 {uint8_t len; /* 字符数组现有长度*/uint8_t alloc; /* 字符数组的已分配空间,不包括结构体和\0结束字符*/unsigned char flags; /* SDS类型*/char buf[]; /*字符数组*/
};
上面代码片段可以看到,len、 alloc 都是uint_t 类型。是一个8位无符号整数,每个占用一个字节的空间,也就是说当字符串类型是 sdshdr8 时,它能表示的字符数组长度2的8次方。
同理, sdshdr16、sdshdr32、sdshdr64 三种类型来说,它们的 len 和 alloc 数据类型分别是 uint16_t、uint32_t、uint64_t,即它们能表示的字符数组长度,分别不超过 2 的 16 次方、32 次方和 64 次方。
说到这里也就大概了解不同的类型的作用了,那么还有一点要提一下,字节对齐方式和紧凑的方式分配内存
对齐和紧凑分配内存
在默认情况下,编译器会按照 8 字节对齐的方式,给变量分配内存。也就是说,即使一个变量的大小不到 8 个字节,编译器也会给它分配 8 个字节。
看下面的两个例子,大概就懂了
include <stdio.h>int main() {struct s1 {char a;int b;} ts1;printf("%lu\n", sizeof(ts1));struct __attribute__((packed)) s2{char a;int b;} ts2;printf("%lu\n", sizeof(ts2));}
char 类型占用 1 个字节,int 类型占用 4 个字节.,sizeof(ts1) ==8 , sizeof(ts2) == 5,剩下的自己琢磨啦
小结
加油、学到就是赚到,哪怕应付面试呢
相关文章:
01、字符传实现为什么是SDS而不是char*?
问题: 1. sds 是什么 ? 2. sds 相对于char * 有什么好处 ?解决了哪些疑难杂症? 3. sds 有什么不足?可以优化的点? 思考下: 平常工作开发中,我们记录一条用户信息、订单信息&…...
顺应趋势,用大数据精准营销抓住大数据时代的机遇
想先问大家一个问题:“你觉得现在的营销好做吗?”想必大多数人在说到自己如何营销这一点上,都有道不完的“苦水”。“现在找客户难,投了几十万的广告费,真正来的客户却少得可怜,平均获客成本高得吓人”一位…...
面向石油和天然气的计算机视觉和深度学习1
面向石油和天然气的计算机视觉和深度学习1 1. 好处1.1 安全1.2 生产优化与估算(Production Optimization and Estimation)1.3 降低生产和维护成本(Reduce Production and Maintenance Costs) 2. 应用2.1 维护2.1.1 预测维护&#…...
微信小程序三种授权登录以及授权登录流程讲解
🎉🎉欢迎来到我的CSDN主页!🎉🎉 🏅我是Java方文山,一个在CSDN分享笔记的博主。📚📚 🌟推荐给大家我的专栏《微信小程序开发实战》。🎯Ἲ…...
C现代方法(第10章)笔记——程序结构
文章目录 第10章 程序结构10.1 局部变量10.1.1 静态局部变量10.1.2 形式参数 10.2 外部变量10.2.1 示例:用外部变量实现栈10.2.2 外部变量的利与弊 10.3 程序块10.4 作用域10.5 构建C程序10.5.1 复杂程序:给一手牌分类 问与答写在最后 第10章 程序结构 …...
解密Web安全:Session、Cookie和Token的不解之谜
解密Web安全:Session、Cookie和Token的不解之谜 前言第一部分:什么是Session、Cookie和Token1. Session(会话):2. Cookie(HTTP Cookie):3. Token(令牌):比较: 第二部分&a…...
1016 部分A+B
#include<bits/stdc.h> using namespace std; int main(){string str1;string str2;int a,b;cin>>str1>>a>>str2>>b;int a10;int a20;for(auto t:str1){if(t-0a){a1a1*10a;}}for(auto t:str2){if(t-0b){a2a2*10b;}}cout<<a1a2; }...
搭建react项目
一、环境准备 1、安装node 官网下载安装:https://nodejs.org/en 注: npm5.2以后,安装node会自动安装npm和npx 2、安装webpack npm install -g webpack3、安装create-react-app npm install -g create-react-app二、创建react项目 1、初…...
Hive跨集群数据迁移过程
文章目录 环境数据迁移需求迁移过程记录 环境 Hive集群AHive集群B跳转机一台 数据迁移需求 本次迁移数据100G,15亿条,数据流转方向从集群A经过跳转机到集群B,通过HDFS拉取和重新建表导入的方式完成数据库迁移。 迁移过程记录 - 当前操作…...
中国移动启动算网大脑“天穹”全网试商用
10月12日,中国移动在2023全球合作伙伴大会主论坛正式启动算网大脑“天穹”全网试商用,全面开启算力网络2.0新征程,标志着中国移动算力网络迈向“融合统一”新阶段。 为落实国家“东数西算”战略,中国移动开创性提出算力网络新理念…...
apk和小程序渗透
apk和小程序域服务器通信使用的还是http协议,只是使用了加密。只要可以获取到http的请求报文,就可以回归到web渗透的层面。apk和小程序的渗透很复杂,涉及逆向时要进行脱壳,脱壳后反编译了,源代码没做加密就能直接逆向出…...
播放svga动画的时候 第一次加载资源,然后切换动画 会动画会重影
如果在切换 SVGA 动画的过程中,第一次加载时出现重影,但第二次及以后的切换没有重影,这可能是由于第一次加载时资源缓存不完整导致的。为了解决这个问题,你可以尝试以下方法: 1.在每次切换动画之前,预先加…...
如何实现前端音频和视频播放?
聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 欢迎来到前端入门之旅!感兴趣的可以订阅本专栏哦!这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…...
chatgpt 4V 识图功能
1.获取图片的sig和file_id 2e0edc6e489ed13a3f32f0dd87527d77.jpg是本地图片的名字 头部认证信息自己F12 抓取 1.获取图片的sighttps://chat.openai.com/backend-api/filesAuthorization:Bearer eyJhbGc****************5V-lztYwLb9hr6LP7g Cookie: **********************…...
展馆导览系统之AR互动式导航与展品语音讲解应用
一、项目背景 随着科技的进步和人们对于文化、艺术、历史等方面需求的提升,展馆在人们的生活中扮演着越来越重要的角色。然而,传统的展馆导览方式,如纸质导览、人工讲解等,已无法满足参观者的多元化需求。为了提升参观者的体验&a…...
Rust 中的String与所有权机制
文章目录 一、string二、所有权2.1 所有权与作用域2.2 对所有权的操作2.2.1 转移2.2.3 拷贝2.2.3 传递 2.3 引用2.3.1 借用2.3.2 可变引用 一、string 之前学习过 Rust 只有几种基础的数据类型,但是没有常用的字符串也就是String,今天来学习一下 String…...
多线程环境下如何安全的使用线性表, 队列, 哈希表
小王学习录 今日鸡汤安全使用ArrayList安全使用队列安全使用HashMap 今日鸡汤 安全使用ArrayList 使用synchronized锁或者reentrantLock锁使用CopyOnWriteArrayList(COW写时拷贝)类来代替ArrayList类. 多个线程对CopyOnWriteArrayList里面的ArrayList进行读操作, 不会发生线程…...
机器人SLAM与自主导航
机器人技术的迅猛发展,促使机器人逐渐走进了人们的生活,服务型室内移动机器人更是获得了广泛的关注。但室内机器人的普及还存在许多亟待解决的问题,定位与导航就是其中的关键问题之一。在这类问题的研究中,需要把握三个重点&#…...
Zookeeper集群 + Kafka集群的详细介绍与部署
文章目录 1. Zookeeper 概述1.1 简介1.2 Zookeeper的工作机制1.3 Zookeeper 主要特点1.4 Zookeeper 数据结构1.5 Zookeeper的相关应用场景1.5.1 统一命名服务1.5.2 统一配置管理1.5.3 统一集群管理1.5.4 服务器动态上下线1.5.5 软负载均衡 1.6 Zookeeper 选举机制1.6.1 第一次启…...
STP、堆叠与VRRP如何使用
✍ STP生成树用在哪里? ✍ STP和堆叠有什么区别? ✍ VRRP双网关热备份如何部署? --- 通过交换机组成网络是局域网,连接终端设备的交换机就是接入层交换机。 --- 如上组网结构单一,不需要网工。 容易发生单点故障&…...
3步搞定视频转PPT:智能提取演示文稿的完整工作流
3步搞定视频转PPT:智能提取演示文稿的完整工作流 【免费下载链接】extract-video-ppt extract the ppt in the video 项目地址: https://gitcode.com/gh_mirrors/ex/extract-video-ppt 视频转PPT工具extract-video-ppt通过智能帧间差异检测技术,帮…...
AI与机器学习:核心技术差异与应用场景解析
1. 概念辨析:AI与机器学习的本质差异当我们在科技媒体上看到"AI医生诊断准确率超过人类"和"机器学习模型预测股票走势"这类标题时,很多人会把这两个术语混为一谈。实际上,人工智能(AI)和机器学习&…...
用Python和MATLAB复现蝴蝶优化算法(BOA):从原理到代码的保姆级教程
蝴蝶优化算法(BOA)全流程实战:Python与MATLAB双版本代码精解 蝴蝶优化算法(Butterfly Optimization Algorithm, BOA)作为新兴的群体智能算法,凭借其独特的生物行为模拟机制和简洁的参数结构,在工程优化、机器学习等领域展现出独特优势。本文将…...
超导体-硅约瑟夫森结技术解析与应用
1. 超导体-硅约瑟夫森结技术解析约瑟夫森结作为连接经典与量子世界的桥梁,其核心在于两个超导体之间形成的弱耦合结构。当我在实验室第一次观察到4.2K温度下NbN/a-Si/NbN结的I-V特性曲线时,那个清晰的能隙电压跳变让我至今难忘。这种超导体-硅-超导体(SC…...
铂力特金属3D打印技术又一突破,三大关键点解读
在TCT亚洲展的铂力特展台,有一幕让笔者印象特别深刻,讲解人员中途突然折返到一版零件前,特意对它进行介绍,足以看出这些零件具有非同寻常的价值。它所代表的,就是铂力特的高精度3D打印解决方案。这版产品是铂力特为华力…...
Stable Diffusion插画创作:从模型选型到商业应用
1. 项目概述:基于Stable Diffusion的插画创作实践去年第一次接触Stable Diffusion时,我用它生成了一张动漫风格的城堡插画,结果得到了一个三只眼睛的扭曲建筑。这个令人啼笑皆非的失败案例,反而让我意识到AI绘画工具在参数设置和提…...
别再手动建模了!3DMAX 2011+ 用户必看:这个螺母螺栓插件,5分钟搞定标准件
3DMAX高效建模革命:参数化螺母螺栓插件深度解析 在机械设计与工业产品建模领域,标准件的重复创建一直是设计师的痛点。传统手动建模一颗符合国标的六角螺母,熟练设计师至少需要15分钟调整参数和检查尺寸,而一个中等复杂度的装配体…...
从阻塞IO到虚拟线程异步编排:一个实时风控网关的毫秒级响应改造,3周上线、0宕机、TP99下降68ms
第一章:从阻塞IO到虚拟线程异步编排:一个实时风控网关的毫秒级响应改造,3周上线、0宕机、TP99下降68ms某支付平台风控网关原基于 Spring Boot 2.7 Tomcat 阻塞模型构建,日均处理 4200 万次规则校验请求,平均响应延迟 …...
AI Agent:从“科幻概念“到“生活必需品“的进化之路
想象一下,如果钢铁侠的贾维斯不再是电影里的幻想,而是每天早上帮你规划日程、处理邮件、甚至帮你写周报——这就是AI Agent正在做的事情。 一、先搞清楚:AI Agent到底是什么? 很多人把AI Agent和ChatGPT混为一谈,这就像把"瑞士军刀"和"瑞士军刀工厂"…...
从一次GPU Direct Storage超时故障说起:深入调试PCIe IDO与宽松排序的配置陷阱
GPU Direct Storage超时故障全解析:PCIe IDO与宽松排序的工程实践 深夜的运维告警铃声突然响起——训练集群中某台搭载A100显卡的节点再次出现GPU Direct Storage数据传输超时。这已经是本周第三次类似故障,每次重启服务后问题暂时消失,但根本…...
