Redis数据结构——Redis简单动态字符串SDS

定义

众所周知,Redis是由C语言写的。
对于字符串类型的数据存储,Redis并没有直接使用C语言中的字符串。
而是自己构建了一个结构体,叫做“简单动态字符串”,简称SDS,比C语言中的字符串更加灵活。

SDS的结构体是这样的:

struct{int len; // 数组中已使用的字节的数量,即真实的内容长度 int free; // 数组中未使用的字节的数量,即还可以继续存储的内容的长度 char buff[]; // 字节数组,用来保存字符串 
};

在C语言中,总是使用长度是N+1的字符数组来保存长度为N的字符串,并且字符数组的最后一个是’\0’结束符,在SDS中,一次申请的字符串的长度比真实的长,所以才会有free这个属性

SDS与C语言字符串相比,优点是:

  • O(1)复杂度获取字符串长度
  • 防止了内存溢出
  • 减少内存分配的次数
  • 二进制安全

获取字符串长度

对于C字符串来说,获取一个字符串的真实长度,需要遍历字符串,这就是O(N)的时间复杂度。
而在SDS中,用一个len属性保存字符串的真实长度,每次对字符串的修改,都会维护这个len属性
因此对于SDS来说,获取字符串的真实长度的时间复杂度是O(1),这确保了获取字符串长度的操作不会成为Redis字符串的性能瓶颈

内存溢出问题

在C字符串中,如果要对字符串进行修改操作,如果忘记了给字符串重新分配足够的空间,就会导致内存溢出问题。在C语言中,并没有内存溢出相关的检查机制,因此可能会导致不可预测的问题产生。

通过SDS的API来操作字符串时,会先检查SDS的空间是否满足修改的要求,如果不满足的话,会自动将SDS的空间扩展至要求的大小,然后执行字符串操作,所以使用SDS来操作字符串,不用担心内存溢出问题。

减少内存分配的次数

对于C语言字符串,因为总是有一个长度为N+1的字符数组来保存一个长度为N的字符串。
所以,如果对C字符串进行操作:

  • 如果进行字符串的长度增长的操作,比如追加字符append,那么在执行这个操作前,需要先为字符串分配新的内存空间,然后才能执行操作。如果忘记了重新分配内存,会导致内存溢出问题。
  • 如果进行字符串长度的减少操作,比如删除某个字符,那么在执行这个操作之后,需要释放掉不再使用的内存空间。如果忘记了释放内存,会导致内存泄漏问题。

而对于SDS来说,不存在这些问题,通过两个机制来解决以上问题

  • 空间预分配
  • 惰性空间释放

1. 空间预分配

空间预分配机制,用来优化SDS字符串的增长操作。
我们认为初始化赋值和拼接操作都是对于SDS的扩容操作。
当SDS来扩容一个字符串时,系统不仅会为SDS分配所需的内存空间大小,还会分配额外的未使用空间,即系统分配给SDS的空间大小比真实的字符串长度要大。
至于,额外的空间有多大,有以下规则:

  • 当扩容后的SDS的长度小于1MB,那么程序分配的额外空间就是len的大小,即与真实的字符串的空间大小相同。例如,扩容后,SDS的len的长度是20,那么额外的空间也是20,总共的SDS的空间是40字节。
  • 如果扩容后的SDS的长度大于等于1MB,那么程序会分配1MB的额外空间。

通过空间预分配策略,可以减少字符串增长操作的内存分配次数。
当进行字符串增长操作时,会先检查free的空间大小是否够增加的长度,如果够,那么直接在真实的数组上操作,无需再进行内存分配操作,并维护free和len的值。
如果不够,那么就会进行扩容操作,扩容机制上面说过了。

2. 惰性空间释放

惰性空间释放用来优化字符串的缩短操作。
当SDS缩短一个字符串时,还是直接在原始的数组上操作,并维护len和free的值。
缩短完成后,程序并不会立即回收释放后的内存,而是使用free属性记录下来,方便下次的字符串长度增加时使用。

二进制安全

C字符串中的字符必须符号某种编码,例如,当编码格式是ASCII时,除了末尾的空字符’\0’外,字符串内容中不可以出现空字符,否则程序在读取字符串时,会误以为这是字符串的结尾。
这样的限制使得,C字符串只能保存文本数据,而不能保存图片、音频等二进制数据。
而SDS会以二进制的方式来处理存放到buff数组中的数据,程序不会对其中的数据进行限制、过滤等额外操作
这就是我们称SDS是字节数组的原因——Redis不是用buff数组来保存字符,而是保存一系列的二进制数据

SDS不是使用空字符’\0’来判断字符串的结尾,而是使用len属性来判断字符串是否结尾
如"Redis\0String",C字符串的函数会把’\0’当做结束符来处理,而忽略到后面的"String"。而SDS的buf字节数组不是在保存字符,而是一系列二进制数组,SDS API都会以二进制的方式来处理buf数组里的数据,使用len属性的值而不是空字符来判断字符串是否结束。

参考文章

Redis数据结构——简单动态字符串SDS - 随心所于 - 博客园
《Redis设计与实现》

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

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

相关文章

Python-OpenCV中的图像处理-视频分析

Python-OpenCV中的图像处理-视频分析 视频分析Meanshift算法Camshift算法光流 视频分析 学习使用 Meanshift 和 Camshift 算法在视频中找到并跟踪目标对象: Meanshift算法 Meanshift 算法的基本原理是和很简单的。假设我们有一堆点(比如直方 图反向投影得到的点&…

ASR 语音识别接口封装和分析

这个文档主要是介绍一下我自己封装了 6 家厂商的短语音识别和实时流语音识别接口的一个包,以及对这些接口的一个对比。分别是,阿里,快商通,百度,腾讯,科大,字节。 zxmfke/asrfactory (github.c…

ubuntu 安装 cuda

ubuntu 安装 cuda 初环境与设备在官网找安装方式 本篇文章将介绍ubuntu 安装 CUDA Toolkit CUDA Toolkit 是由 NVIDIA(英伟达)公司开发的一个软件工具包,用于支持并优化 GPU(图形处理器)上的并行计算和高性能计算。它…

解析TCP/IP协议的分层模型

了解ISO模型:构建通信的蓝图 为了促进网络应用的普及,国际标准化组织(ISO)引入了开放式系统互联(Open System Interconnect,OSI)模型。这个模型包括了七个层次,从底层的物理连接到顶…

一、Dubbo 简介与架构

一、Dubbo 简介与架构 1.1 应用架构演进过程 单体应用:JEE、MVC分布式应用:SOA、微服务化 1.2 Dubbo 简介一种分布式 RPC 框架,对专业知识(序列化/反序列化、网络、多线程、设计模式、性能优化等)进行了更高层的抽象和…

ArcGIS Maps SDK for JavaScript系列之三:在Vue3中使用ArcGIS API加载三维地球

目录 SceneView类的常用属性SceneView类的常用方法vue3中使用SceneView类创建三维地球项目准备引入ArcGIS API创建Vue组件在OnMounted中调用初始化函数initArcGisMap创建Camera对象Camera的常用属性Camera的常用方法 要在Vue 3中使用ArcGIS API for JavaScript加载和展示三维地…

【JavaSE】面向对象之封装

封装的概念 封装是把过程和数据包围起来,对数据的访问只能通过已定义的接口。面向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象。封装是一种信息隐藏技术&#xff…

Java旋转数组中的最小数字(图文详解版)

目录 1.题目描述 2.题解 分析 具体实现 方法一(遍历): 方法二(排序): 方法三(二分查找): 1.题目描述 有一个长度为 n 的非降序数组,比如[1,2,3,4,5]&a…

Linux基础

Linux 一、基础01- 执行环境准备02- linux的版本分类02.1 内核版本02.2 发行版本02.3 内核和发行版本的区别: 03- 虚拟机安装04- 启动linux 二、系统操作05- 帮助命令05.1 man 帮助05.2 help 帮助05.2.1 内部命令05.2.2 外部命令 05.3 info 帮助 06- ls命令06.1 -r06.2 -rt06.3…

npm install 中 --save 和 --save-dev 是什么?

npm,全名 Node Package Manager,套件管理工具,package.json 会记下你在项目中安装的所有套件。 假设在项目中安装 lodash npm i --save lodash这样在 dependencies 中会出现: 如果修改了导入方式: npm i --save-dev …

在Linux中对docker 一键安装,本地安装,无网络安装,

在Linux中对docker 一键安装 前提先准备好安装包 非常丝滑 首先先把需要准备的文件准备好,/package/base.tar 和 /package/docker-20.10.10.tgz包 这两个文件包必须放在 /package目录下 再和/package同级的目录下再准备conf目录,conf目录下放docker.se…

Labview解决“重置VI:xxx.vi”报错问题

文章目录 前言一、程序框图二、前面板三、问题描述四、解决办法 前言 在程序关闭前面板的时候小概率型出现了 重置VI:xxx.vi 这个报错,并且发现此时只能通过任务管理器杀掉 LabVIEW 进程才能退出,这里介绍一下解决方法。 一、程序框图 程序…

特征选择 | 递归特征消除算法筛选最优特征

特征选择 | 递归特征消除算法筛选最优特征 目录 特征选择 | 递归特征消除算法筛选最优特征写在前面常规方法算法原理结果分析参考资料 写在前面 在实际应用中,特征选择作为机器学习和数据挖掘领域的重要环节,对于提高模型性能和减少计算开销具有关键影响…

pve7.2虚拟机 lvm磁盘扩容,增加硬盘操作

之前安装pve时候只有256的ssd,最近安装的虚拟机较多,给加块闲置硬盘,顺便学习一下,像pve这种虚拟机系统,硬盘应该可以像nas你这样随时增加,而不影响上层应用,我自己也是摸索着做。 一、安装好硬盘后打开pv…

vue3+ts-tsconfig.json报错Option ‘importsNotUsedAsValues’

vue3ts-tsconfig.json报错Option ‘importsNotUsedAsValues’ is deprecated and will stop functioning in TypeScript 5.5. Specify compilerOption ‘“ignoreDeprecations”: “5.0”’ to silence this error. Use ‘verbatimModuleSyntax’ instead 自我记录 翻译 选项…

智能家居(2)---串口通信(语音识别)控制线程封装

封装语音线程&#xff08;语音通过串口和主控设备进行交流&#xff09;实现对智能家居中各种灯光的控制 mainPro.c(主函数) #include <stdio.h> #include "controlDevice.h" #include "inputCommand.h" #include <pthread.h>struct Devices …

echart 3d立体颜色渐变柱状图

如果可以实现记得点赞分享&#xff0c;谢谢老铁&#xff5e; 1.需求描述 根据业务需求将不同的法律法规&#xff0c;展示不同的3d立体渐变柱状图。 2.先看下效果图 3. 确定三面的颜色&#xff0c;这里我是自定义的颜色 // 右面生成颜色const rightColorArr ref(["#79D…

ComponentOne Studio ASP.NET MVC Crack

ComponentOne Studio ASP.NET MVC Crack FlexReport增强功能 添加了对在Microsoft Windows上部署Microsoft Azure的支持。 添加了对显示嵌入字体的支持。 .NET标准版的经典C1PDF(Beta版) GrapeCity的经典C1Pdf库现在提供了基于Microsoft.NET标准的版本。在任何.NET应用程序(包括…

每日一学——IP寻址

IP寻址是指在网络中分配和识别设备的唯一IP地址。IP地址是由一串数字组成的标识符&#xff0c;用于在网络中定位和识别设备。 IPv4是最常用的IP地址版本&#xff0c;它由32位的地址组成&#xff0c;通常表示为四个以点分隔的十进制数字&#xff08;例如192.168.0.1&#xff09…

江南大学计算机考研分析

24计算机考研|上岸指南 江南大学 江南大学计算机考研招生学院是人工智能与计算机学院。目前均已出拟录取名单。 江南大学人工智能与计算机学院成立于2020年3月&#xff0c;办学历史可追溯到1994年设立的计算机应用专业。学院秉持江南大学“彰显轻工特色&#xff0c;服务国计民…