Redis源码之SDS简单动态字符串
Redis 是内存数据库,高效使用内存对 Redis 的实现来说非常重要。
看一下,Redis 中针对字符串结构针对内存使用效率做的设计优化。

一、SDS的结构
c语言没有string类型,本质是char[]数组;而且c语言数组创建时必须初始化大小,指定类型后就不能改变,并且字符数组的最后一个元素总是空字符 '\0' 。
以下展示了一个值为 "Redis" 的 C 字符串:

Redis没有直接使用C语言的字符串方式,而是构建了一种简单动态字符串(Simple dynamic string, SDS)的类型,Redis中的字符串底层都是使用SDS结构进行存储,比如包含字符串的键值对底层都是使用SDS结构实现的。
SDS结构定义在sds.h中
struct sdshdr{
int len;//SDS保存的字符串长度
int free;//buf数组中未使用字节数量
char buf[];//字符数组,保存字符串
}

最后一个字节保存了空字符'\0',保留了C字符串的规范,使得SDS结构的字符串,可以重用一部分C函数库的函数。
二、为什么不使用C字符串
主要是因为C字符串有以下缺点:
- 获取字符串长度时间复杂度为O(N):C字符串获取长度需遍历整个字符串,遇到'\0'空字符为止。
- 缓冲区溢出:比如在进行字符串追加操作时,如果没有分配足够的内存,就会造成内存溢出。
- 内存重分配:每次增长或者截短字符串,程序都要对保存C字符串的数组进行内存重分配操作,而内存重分配涉及复杂的算法,并可能需要执行系统调用,所以它通常比较耗时。
- 空字符问题:C字符串中间不能保存空格,否则程序遍历是会误认为是字符串的末尾。这一限制导致C字符串只能存储文本数据,不能保存像图片、音视频、压缩文件等二进制数据。
三、怎样解决C字符串问题

1、SDS通过len属性记录了SDS长度,所以获取长度的时间复杂度为O(1),即strlen命令的时间复杂度是O(1)。
2、SDS空间分配策略避免了缓冲区溢出:当对SDS进行修改时,会先检查SDS空间是否满足修改,不满足会自动扩展到所需大小,然后才执行修改。
3、较少修改字符串时内存重分配次数:SDS中的free记录buf字节数组中未使用的字节。
redis通过free属性实现空间预分配、惰性空间释放两种优化策略。
- 空间预分配:当对SDS进行增长操作时,程序不仅会分配修改所必须得空间,还会为SDS分配额外的未使用空间。通过预分配策略,减少了连续执行字符串增长操作时内存重分配次数。
- 惰性空间释放:当对SDS进行截短操作时,程序并不会立即回收缩短后多出来的字节所占用的内存,而是使用free属性记录多出来的字节数,以供将来使用。如果将来要对这个SDS进行增长操作,未使用空间可能就派上用场,并且增长操作也不一定会执行内存重分配。
SDS结构中的buf字节数组,是二进制安全的,不仅可以保存字符,也可以保存二进制数据。
SDS保留了C字符串的惯例,将数据的末尾设置为空字符'\0',SDS中之所以保留这一规范是可以重用C字符串函数库的一部分函数,例如追加字符串。
四、对字符串的进一步优化
Redis string的三种编码:
- int 存储8个字节的长整型(long,2^63-1 )
- embstr, embstr格式的SDS (Simple Dynamic String)
- raw, raw格式的SDS,存储大于44个字节的长字符串
int类型就是指的是数字,那么raw、embstr都代表的是字符串有什么异同吗,下面我们分析下。

图中展示了两者的区别,可以看到embstr将redisObject和SDS保存在连续的64字节空间内,这样可以只需要一次内存分配,而对于raw来说,SDS和redisObject分离,需要两次内存分配,而且占用更多的内存空间。

可以看到embstr在3.2+中使用了叫sdshdr8的结构,在该结构下,元数据只需要3个字节,而Redis需要8个字节,所以总共64个字节,减去redisObject(16字节),再减去SDS的原信息,最后的实际内容就变成了44字节和39字节。

当字符串小于等于 44 字节时,Redis 就使用了嵌入式字符串的创建方法,以此减少内存分配和内存碎片。
下面这张图展示了 createEmbeddedStringObject 创建嵌入式字符串的过程:

总之,只要记住,Redis 会通过设计实现一块连续的内存空间,把 redisObject 结构体和 SDS 结构体紧凑地放置在一起。
这样一来,对于不超过 44 字节的字符串来说,就可以避免内存碎片和两次内存分配的开销了。
Redis源码之SDS简单动态字符串的更多相关文章
- Redis源码阅读一:简单动态字符串SDS
源码阅读基于Redis4.0.9 SDS介绍 redis 127.0.0.1:6379> SET dbname redis OK redis 127.0.0.1:6379> GET dbn ...
- redis源码学习_简单动态字符串
SDS相比传统C语言的字符串有以下好处: (1)空间预分配和惰性释放,这就可以减少内存重新分配的次数 (2)O(1)的时间复杂度获取字符串的长度 (3)二进制安全 主要总结一下sds.c和sds.h中 ...
- 跟着大彬读源码 - Redis 7 - 对象编码之简单动态字符串
Redis 没有直接使用 C 语言传统的字符串表示(以空字符串结尾的字符数组),而是构建了一种名为简单动态字符串(simple dynamic string)的抽象类型,并将 SDS 用作 Redis ...
- redis 系列3 数据结构之简单动态字符串 SDS
一. SDS概述 Redis 没有直接使用C语言传统的字符串表示,而是自己构建了一种名为简单动态字符串(simple dynamic string, SDS)的抽象类型,并将SDS用作Redis的默 ...
- 小白的Redis学习(一)-SDS简单动态字符串
本文为读<Redis设计与实现>的记录.该书以Redis2.9讲解Redis相关内容.请注意版本差异. Redis使用C语言实现,他对C语言中的char类型数据进行封装,构建了一种简单动态 ...
- Redis数据类型之SDS简单动态字符串
一,简单的动态字符串 1,Redis自己构建了一种名为简单动态字符串的抽象类型,并将SDS用作Redis的默认字符串表示, 2,在redis的数据库里面,包含字符串值的键值对在底层都是由SDS实现的 ...
- 关于redis中SDS简单动态字符串
1.SDS 定义 在C语言中,字符串是以’\0’字符结尾(NULL结束符)的字符数组来存储的,通常表达为字符指针的形式(char *).它不允许字节0出现在字符串中间,因此,它不能用来存储任意的二进制 ...
- Redis源码阅读-sds字符串源码阅读
redis使用sds代替char *字符串, 其定义如下: typedef char *sds; struct sdshdr { unsigned int len; unsigned int free ...
- redis源码之SDS
1:SDS介绍 我们在redis中执行命令 set key name 的时候,key和name都是字符串类型,而且字符串(string)在redis中是会经常用到的类型,那redis是如何保存字符串的 ...
- Redis设计与实现 (一): 简单动态字符串
1.定义 Redis没有使用C语言的字符串, C语言的字符串只会用在不需要对字符串修改而只使用其值地方. Redis使用SDS表示字符串, 结构定义 : typedef char *sds; str ...
随机推荐
- getElementsByClassName()获取不到值
在这种方式下,虽然使用了getElementsByClassName方法,但是并不能获得到值.从执行顺序上来说,在HTML还没有执行的时候JS就已经开始执行了,所以获得的值不能够获得到.因此,如果遇到 ...
- curl curl请求
$params=[ "a"=>"test", "b"=>"测试" ]; $header=[ "Co ...
- [读书笔记]FDTD与YEE晶胞
截图选自Understanding the Finite-Difference Time-Domain Metho 作者是John B. Schneider 有限差分时域(FDTD)方法使用有限差分 ...
- Training time_SSM
三阶段 MyBatis 1 三层架构介绍 2 MyBatis介绍 类库:对于现有技术的一个封装. 框架:对于一个问题的一整套解决方案. MyBatis是一个半自动的ORM持久层的框架.刚开始叫做iBa ...
- 磊磊零基础打卡算法:day16 c++ Trie树
5.19 Trie树: 用处:快速的查找和高效存储字符串集合的数据结构. 类似如此的查找,存储 其简单的两个操作:插入和删除 插入: void insert(char str[]) { int p; ...
- arpspoof、driftnet工具使用
一.arpspoof.driftnet工具安装: 在kali liux中: 安装命令:apt install dsniff apt install driftnet 二.使用arpspoof ...
- Linux中profile、bashrc、bash_profile之间的区别和联系(转)
/etc/profile:此文件为系统的每个用户设置环境信息,当用户第一次登录时,该文件被执行.并从/etc/profile.d目录的配置文件中搜集shell的设置. 英文描述为: # /etc/pr ...
- Unity2017-HTC项目串流Pico摇杆移动功能
最近公司PC项目需要串流到Piconec3上运行,HTC手柄是圆盘键按下移动还可以,但是Piconeo3是摇杆,按下移动的话显得不科学,所以写了一套基于圆盘键,使用摇杆移动的方法 第一步:编写摇杆左右 ...
- 一篇文章带你快速入门学习RPA
大纲: 什么是RPA? RPA的应用领域有哪些? RPA机器人技术一般用于什么行业? RPA的市场需求是什么? RPA项目的实施过程? RPA的未来趋势怎么样? 什么是RPA? RPA 全称& ...
- GO语言学习笔记-反射篇 Study for Go ! Chapter nine - Reflect
持续更新 Go 语言学习进度中 ...... GO语言学习笔记-类型篇 Study for Go! Chapter one - Type - slowlydance2me - 博客园 (cnblogs ...