当前位置: 首页 > news >正文

简单动态字符串 sds

        Redis 设计了简单动态字符串(Simple Dynamic String,SDS)的结构,用来表示
字符串。相比于 C 语言中的字符串实现,SDS 这种字符串的实现方式,会提升字符串的操
作效率,并且可以用来保存二进制数据

为什么 Redis 不用 char*?

首先得先了解char* 字符串数组的结构特点,还有 Redis对字符串的需求是什么,所以下面我们就来具体分析一下

char* 的结构设计

char*字符数组的结构很简单,就是一块连续的内存空间,依次存放了字符串中的每一个
字符。比如,下图显示的就是字符串“redis”的char*数组结构。、

 从图中可以看到,字符数组的最后一个字符是“\0”,这个字符的作用是什么呢?其实,C
语言在对字符串进行操作时,char* 指针只是指向字符数组的起始位置,而字符数组的结尾
位置就用“\0”表示,意思是指字符串的结束

c语言标准库呢就是检查字符这个字符是不是\0然后不是加一往下走,是的话结束

创建了两个字串变量 a 和 b,分别给它们赋值为“red\0is”和“redis\0”。然后,我用 strlen 函数
计算这两个字符串长度,如下所示:

 当程序执行完这段代码后,输出的结果分别是 3 和 5。

也就是说,char* 字符串以“\0”表示字符串的结束,其实会给我们保存数据带来一定的负
面影响。如果我们要保存的数据中,本身就有“\0”,那么数据在“\0”处就会被截断,
而这就不符合 Redis 希望能保存任意二进制数据的需求了

操作函数复杂度

用char* 会导致复杂度增加

字符串追加函数 strcat 和上边的strlen函数都需要遍历到字符串的末尾


SDS 的设计思想

因为 Redis 是使用 C 语言开发的,所以为了保证能尽量复用 C 标准库中的字符串操作函
数,Redis 保留了使用字符数组来保存实际的数据。但是,和 C 语言仅用字符数组不同,
Redis 还专门设计了 SDS(即简单动态字符串)的数据结构

SDS 结构设计

首先,SDS 结构里包含了一个字符数组 buf[],用来保存实际数据。同时,SDS 结构里还
包含了三个元数据,分别是字符数组现有长度 len、分配给字符数组的空间长度 alloc,以
及 SDS 类型 flags
。其中,Redis 给 len 和 alloc 这两个元数据定义了多种数据类型,进
而可以用来表示不同类型的 SDS

在 Redis 源码中查找过 SDS 的定义,那你可能会看到,Redis 使用 typedef
给 char* 类型定义了一个别名,这个别名就是 sds 

typedef char *sds;

因为 SDS 本质还是字符数组,只是在字符数组基础上增加了额外的元数据。在
Redis 中需要用到字符数组时,就直接使用 sds 这个别名。

在创建新的字符串时,Redis 会调用 SDS 创建函数 sdsnewlen。sdsnewlen 函数
会新建 sds 类型变量(也就是 char* 类型变量),并新建 SDS 结构体,把 SDS 结构体中

的数组 buf[] 赋给 sds 类型变量。最后,sdsnewlen 函数会把要创建的字符串拷贝给 sds
变量。下面的代码就显示了 sdsnewlen 函数的这个操作逻辑,你可以看下

 SDS操作效率

因为它本身的数据结构

紧凑型字符串结构的编程技巧

SDS 结构中有一个元数据 flags,表示的是 SDS 类型

事实上,SDS 一共设计了 5 种类型,分别是 sdshdr5、sdshdr8、sdshdr16、sdshdr32 和 sdshdr64

这 5种类型的主要区别就在于,它们数据结构中的字符数组现有长度 len 和分配空间长度
alloc,这两个元数据的数据类型不同。

因为 sdshdr5 这一类型 Redis 已经不再使用了,主要来了解下剩余的 4 种类
型。以 sdshdr8 为例,它的定义如下所示

 我们可以看到,现有长度 len 和已分配空间 alloc 的数据类型都是 uint8_t。uint8_t 是 8
位无符号整型,会占用 1 字节的内存空间。当字符串类型是 sdshdr8 时,它能表示的字符
数组长度(包括数组最后一位\0)不会超过 256 字节(2 的 8 次方等于 256)。

而对于 sdshdr16、sdshdr32、sdshdr64 三种类型来说,它们的 len 和 alloc 数据类型分
别是 uint16_t、uint32_t、uint64_t,即它们能表示的字符数组长度,分别不超过 2 的 16
次方、32 次方和 64 次方。这两个元数据占用的内存空间在 sdshdr16、sdshdr32、
sdshdr64 类型中,则分别是 2 字节、4 字节和 8 字节。

SDS 之所以设计不同的结构头(即不同类型),是为了能灵活保存不同大小的字
符串,从而有效节省内存空间

除了设计不同类型的结构头,Redis 在编程上还使用了专门的编译优化来节省内存
空间。在刚才介绍的 sdshdr8 结构定义中,我们可以看到,在 struct 和 sdshdr8 之间使
用了__attribute__ ((__packed__)),如下所示:

struct __attribute__ ((__packed__)) sdshdr8

其实这里,__attribute__ ((__packed__))的作用就是告诉编译器,在编译
sdshdr8 结构时,不要使用字节对齐的方式,而是采用紧凑的方式分配内存。这是因为在
默认情况下,编译器会按照 8 字节对齐的方式,给变量分配内存。也就是说,即使一个变
量的大小不到 8 个字节,编译器也会给它分配 8 个字节

到这里呢需要了解一下,字节对齐是为什么?

相关文章:

简单动态字符串 sds

Redis 设计了简单动态字符串(Simple Dynamic String,SDS)的结构,用来表示 字符串。相比于 C 语言中的字符串实现,SDS 这种字符串的实现方式,会提升字符串的操 作效率,并且可以用来保存二进制数据…...

“深入剖析JVM内部原理:解密Java虚拟机的奥秘“

标题:深入剖析JVM内部原理:解密Java虚拟机的奥秘 摘要:本文将深入探讨Java虚拟机(JVM)的内部原理,包括其架构、内存管理、垃圾回收机制、即时编译器等关键组成部分。通过解密JVM的奥秘,我们将更…...

使用QT纯代码创建(查找)对话框详细步骤与代码

一、创建项目文件 打开Qt Creator->文件->新建文件或项目->选择Qt Widgets Application 为项目起名字 输入类的名字 二、 了解每个文件的作用 项目创建完毕之后就会出现以下几个文件,先来分别介绍以下这些文件的作用。 Headers->finddialog.h——很显…...

4945: 二进制转十进制

4945: 二进制转十进制 时间限制: 1.000 Sec 内存限制: 128 MB 提交: 520 解决: 335 [命题人:][下载数据: 30] 提交状态报告 题目描述 将二进制数转成十进制输出 输入 一行,一个二进制数,二进制数的位数小于32位。 输出 一个十进制的整数。…...

java后端技术汇总 + 中间件 + 架构思想

1. 华为OD机考题 答案 2023华为OD统一考试(AB卷)题库清单-带答案(持续更新) 2023年华为OD真题机考题库大全-带答案(持续更新) 2. 面试题 一手真实java面试题:2023年各大公司java面试真题汇总--…...

《机器学习系统:设计与实现》读书笔记一

最近几年一直在做算法工程的工作,对机器学习系统有所涉猎,也很感兴趣。近期发现一本开源书籍《机器学习系统:设计与实现》。去图书馆找了它的纸质版,发现内容不尽相同。在这里结合两者做一个读书笔记。本文是第一篇,主…...

C语言单链表OJ题(较难)

一、链表分割 牛客网链接 题目描述: 现有一链表的头指针 ListNode* pHead,给一定值x,编写一段代码将所有小于x的结点排在其余结点之前,且不能改变原来的数据顺序,返回重新排列后的链表的头指针。 思路:…...

工业巡检ar沉浸式互动培训体验实现更加直观、生动的流程展示

以往的工业手工巡检效率极低,错误率偏高,漏检问题严重,会因为现场人员对机械设备的早期维护、操作不会,而影响正常交付和服务,智慧工业是工业智能化和信息化的重要体现,在巡检方面自然也要同步提升&#xf…...

【Spring】核心容器——依赖自动装配

Spring容器根据bean所依赖的资源在容器中自动查找并注入bean的过程叫做自动装配自动装配的方式 1、按类型 2、按名称(耦合性较高) 3、按构造方法 自动装配特点 1、自动装配用于对引用类型进行依赖注入,不能对简单类型进行操作 2、自动装配的…...

TestNG和Junit5测试框架梳理

一、testNG 1. testNG优势 注解驱动: TestNG 使用注解来标识测试方法、测试类和配置方法,使得测试更具可读性。 并行执行: TestNG 支持多线程并行执行测试,可以加速测试套件的执行。 丰富的配置: 可以通过 XML 配置文…...

算法练习Day46|139.单词拆分

LeetCode:139.单词拆分 139. 单词拆分 - 力扣(LeetCode) 1.思路 字符串是否能被字符串列表中的元素拼接出来,显然是一个背包问题,而且需要排列。 将字典转换为HashSet,利用.contains()方法判断是否存在元素与背包中的子串相同…...

Maven工程的安装配置及搭建(集成eclipse完成案例,保姆级教学)

目录 一.下载及安装及环境配置 1.下载及安装 2.环境变量的配置 3.检测是否安装成功 4.配置Maven 1.更换本地仓库 2. 配置镜像 二.集成eclipse完成案例 1.eclipse前期配置Maven 2.创建Maven工程 一.下载及安装及环境配置 1.下载及安装 下载地址:Maven – Down…...

82 | Python可视化篇 —— Plotly数据可视化

文章目录 什么是 Plotly安装 Plotly创建散点图创建线图创建条形图创建饼图创建热力图3D图(3D Plot)直方图(Histogram)3D表面图(3D Surface Plot)箱线图(Box Plot)散点地图(Scatter Map)量级地图(Choropleth Map)在网页中嵌入 Plotly 图表总结什么是 Plotly Plotly…...

Golang 包详解以及go mod

Golang 中包的介绍和定义 包(package)是多个 Go 源码的集合,是一种高级的代码复用方案,Go 语言为我们提供了 很多内置包,如 fmt、strconv、strings、sort、errors、time、encoding/json、os、io 等。 Golang 中的包可以分为三种:1、系统内置包 2、自定义包 3、第三方包…...

中级课程-SSRF(CSRF进阶)

文章目录 成因危害挖掘 成因 危害 挖掘...

C++命名空间

目录 格式 使用 命名空间的嵌套 使用 using声明 命名空间里面包含了逻辑结构上相互关联的一组类、函数、模板等。命名空间像是一个容器,把某些在逻辑结构上相关的 “ 对象 ” 放在一起并与外界区分。特别的,命名空间里的变量名或类名可以和命名空间外…...

阿里云服务器搭建Magento电子商务网站图文教程

本文阿里云百科分享使用阿里云服务器手动搭建Magento电子商务网站全流程,Magento是一款开源电商网站框架,其丰富的模块化架构体系及拓展功能可为大中型站点提供解决方案。Magento使用PHP开发,支持版本范围从PHP 5.6到PHP 7.1,并使…...

Docker安装 Kibana

目录 前言安装Kibana步骤1:准备1. 安装docker2. 搜索可以使用的镜像。3. 也可从docker hub上搜索镜像。4. 选择合适的redis镜像。 步骤2:拉取 kibana 镜像拉取镜像查看已拉取的镜像 步骤3:创建容器创建容器方式1:快速创建容器 步骤…...

数字图像处理 --- 相机的内参与外参(CV学习笔记)

Pinhole Camera Model(针孔相机模型) 针孔相机是一种没有镜头、只有一个小光圈的简单相机。 光线穿过光圈并在相机的另一侧呈现倒立的图像。为了建模方便,我们可以把物理成像平面(image plane)上的图像移到实际场景(3D object)和焦点(focal p…...

基于新浪微博海量用户行为数据、博文数据数据分析:包括综合指数、移动指数、PC指数三个指数

基于新浪微博海量用户行为数据、博文数据数据分析:包括综合指数、移动指数、PC指数三个指数 项目介绍 微指数是基于海量用户行为数据、博文数据,采用科学计算方法统计得出的反映不同事件领域发展状况的指数产品。微指数对于收录的关键词,在指…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误

HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误,它们的含义、原因和解决方法都有显著区别。以下是详细对比: 1. HTTP 406 (Not Acceptable) 含义: 客户端请求的内容类型与服务器支持的内容类型不匹…...

高频面试之3Zookeeper

高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个?3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制(过半机制&#xff0…...

企业如何增强终端安全?

在数字化转型加速的今天,企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机,到工厂里的物联网设备、智能传感器,这些终端构成了企业与外部世界连接的 “神经末梢”。然而,随着远程办公的常态化和设备接入的爆炸式…...

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)

本期内容并不是很难,相信大家会学的很愉快,当然对于有后端基础的朋友来说,本期内容更加容易了解,当然没有基础的也别担心,本期内容会详细解释有关内容 本期用到的软件:yakit(因为经过之前好多期…...

return this;返回的是谁

一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请,不同级别的经理有不同的审批权限: // 抽象处理者:审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...

Selenium常用函数介绍

目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...

华为OD机考-机房布局

import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...

Go语言多线程问题

打印零与奇偶数(leetcode 1116) 方法1:使用互斥锁和条件变量 package mainimport ("fmt""sync" )type ZeroEvenOdd struct {n intzeroMutex sync.MutexevenMutex sync.MutexoddMutex sync.Mutexcurrent int…...

为什么要创建 Vue 实例

核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...

Golang——7、包与接口详解

包与接口详解 1、Golang包详解1.1、Golang中包的定义和介绍1.2、Golang包管理工具go mod1.3、Golang中自定义包1.4、Golang中使用第三包1.5、init函数 2、接口详解2.1、接口的定义2.2、空接口2.3、类型断言2.4、结构体值接收者和指针接收者实现接口的区别2.5、一个结构体实现多…...