Redis设计与实现 -- 动态字符串对象(SDS)
1. 动态字符串( simple dynamic string, SDS)
在 Redis 中,当需要可以被重复修改的字符串时,会使用 SDS 类型 ,而不是 C 语言中默认的 C 字符串类型 。举个例子:
SET msg "Hello World"
在这个语句中,Redis 会新建一个键值对,其中
- key 为一个 字符串,对象的底层实现是一个保存着字符串 “msg” 的 SDS 对象。
- value 为一个字符串,对象的底层实现是一个保存着字符串 “Hello World” 的 SDS 对象。
如果是 key 对多个 value 的情况下, 如
RPUSH fruits "apple" "banana" "cherry"
Redis 同样会新建一个键值对,其中
- key 为一个 字符串,对象的底层实现是一个保存着字符串 “fruits” 的 SDS 对象。
- value 为一个列表对象,包含着三个字符串对象,分别由三个 SDS 实现:第一个 SDS 保存着字符串 “apple” ,第二个 SDS 保存着字符串 “banana”,第三个 SDS 保存着字符串 “cherry”。
SDS 对象除了作为保存数据库值的字符串之外,还用作缓冲区,比如 AOF 模块中的 AOF 缓冲区,客户端的输入缓冲区等等。
2. SDS 的定义

SDS 的结构如上图所示,len 表示字符串长度,需要注意的是,SDS 的 buf 数组也和 C 字符串一样保留着最后的空字符,而它的 len 计算时不会加上这个空字符,所以 "Redis\0" 在这里 len 值为 5。由于封装的原因,这里的 ‘\0’ 实际上虽然存在,但是使用者是完全不知道这里还有一个空字符的存在的,而这里加上空字符的原因也是为了可以让 SDS 沿用 一些 C 字符串函数库的函数。比如可以直接用 printf("%s", s->buf); 来打印 SDS 的字符串。
3. SDS 和 C 字符串的区别
- 传统的 C 字符串中,我们如果想要知道它的长度,需要遍历其一遍,直到遇到空字符为止,时间复杂度为 O(N)。而 SDS 可以直接取 len 的值,时间复杂度为 O(1)。
- 由于没有字符串的长度,C 字符串很容易导致缓冲区溢出,访问越界问题。
- C 字符串在使用的时候遇到空字符 ‘\0’ 便会认为字符串已经结束,一旦字符串中含有该字符则会导致字符串的异常截断,而 SDS 利用 len 字段可以防止这个情况。所以通过使用二进制安全的 SDS 可以存取任意数据。
- 拼接字符串时,C 字符串需要内存重分配拓展数组的大小,防止缓冲区溢出,截断字符串时,C 字符串需要内存重分配释放截断部分,防止内存泄漏。而 Redis 为了提高性能,增加了一个未使用空间的字段,通过该字段实现了空间预分配和惰性空间释放两种优化策略。
空间预分配策略:每次 SDS 进行空间扩展的时候,程序除了修改当前已有的空间,还会为 SDS 分配额外的空间,这也是 free 字段的作用,记录额外空间的长度,额外空间的长度分配有两种策略:根据修改后 SDS 的长度,即 len 属性的值,len 小于 1 MB 时,分配和 len 一样大小的额外空间, len 大于 1MB 时,则 分配 1 MB 的额外空间。举个例子, SDS 为 13 个字节,扩展 2 个字节后,len 变成 15 个字节,这时会分配额外的 15 个字节,free = 15;这时实际的字符串长度为 15 + 15 + 1 = 31。
惰性空间释放策略:当 SDS 进行缩短字符串的操作时,不马上进行内存重分配,而是利用 free 字段将这些截断的字符串长度记录下来,举个例子,如下图所示的 SDS 结构

如果我们试图删除所有 “X” 和 “Y” 的字符, SDS 的结构会变成如下图所示。

可以看到所有 “X” 和 “Y” 的字符长度总计为 8 ,这里 free 的值也被修改为 8。这时如果再对字符串进行增长的操作,则可以防止进行内存重分配的操作,而直接使用前面 “节约” 下来的内存。
4. 总结

之前遇到过一个二进制数据存取的问题 ,将 protobuf 序列化成二进制存到 Redis 数据库,然后取出来的时候并没有正常取出整个字符串,导致数据取出来之后反序列化异常,对比了存取时字符串的长度,发现就是因为 '\0' 字符导致的,后面通过加上字符串的长度去取字符串解决了这个问题。
Redis设计与实现 -- 动态字符串对象(SDS)的更多相关文章
- Redis数据结构之简单动态字符串SDS
Redis的底层数据结构非常多,其中包括SDS.ZipList.SkipList.LinkedList.HashTable.Intset等.如果你对Redis的理解还只停留在get.set的水平的话, ...
- Redis—简单动态字符串(SDS)
目录 Redis-简单动态字符串(SDS) SDS的定义 SDS与C字符串的区别 1. 常数复杂度获取字符串长度: 2. 杜绝缓冲区溢出: 3. 减少修改字符串时带来的内存重分配次数 4. 二进制安全 ...
- Redis 数据结构之简单动态字符串SDS
几个概念1:key对象 数据库存储键值对的键,总是一个字符串对象.2:value对象 数据库存储键值对的值,可以是字符串对象,list对象,hash对象,set对象,sorted set对象. ...
- redis之简单动态字符串(SDS)
O(N):时间复杂度 N的值越大 时间复杂度随N的平方增大 O(1):就是说N很大的时候,复杂度基本不增长了.基本就是常量了. 在Redis数据库里 包含字符串值的键值对 在底层都是由SDS实现的. ...
- Redis 设计与实现:字符串 SDS
本文的分析没有特殊说明都是基于 Redis 6.0 版本源码 redis 6.0 源码:https://github.com/redis/redis/tree/6.0 在 Redis 中,字符串都用自 ...
- Redis核心原理-简单动态字符串SDS
SDS简介 Redis是C语言编写的,但没有使用c语言的字符串结构,而是自己实现了一套简单动态字符串 simple dynamic string 简称SDS,SDS兼容C语言的字符串类型,原理类似Ja ...
- Redis中的简单动态字符串
Redis没有直接使用C语言传统的字符串表示(以空字符结尾的字符数组,以下简称C字符串),而是自己构建了一种名为简单动态字符串(simple dynamic string,SDS)的抽象类型,并将SD ...
- redis 笔记01 简单动态字符串、链表、字典、跳跃表、整数集合、压缩列表
文中内容摘自<redis设计与实现> 简单动态字符串 1. Redis只会使用C字符串作为字面量,在大多数情况下,Redis使用SDS(Simple Dynamic String,简单动态 ...
- 简单动态字符串(SDS)
SDS 前提:在redis中,C字符串只会作为字符串字面量用在一些无须对字符串进行修改的地方,比如打印日志: redisLog(REDIS_WARNING, “Redis is ready to ex ...
随机推荐
- Linux--shell交互输入与循环语句--06
一.交互输入 1.命令用法:read a b c -> aa bb cc read命令同时可以定义多个变量值:而输入的内容默认以空格为分隔符,将值输入到对应的变量中:如果默认值输入过多,最后 ...
- Firewalld--02 端口访问/转发、服务访问、源地址管理
目录 防火墙端口访问/转发.服务访问.源地址管理 1. 防火墙端口访问策略 2. 防火墙服务访问策略 3.防火墙接口管理 4.防火墙源地址管理 5. 防火墙端口转发策略 防火墙端口访问/转发.服务访问 ...
- mysql查询时间戳转换
mysql查询时间戳转换 SELECT FROM_UNIXTIME(create_time) FROM tablename; 更新时间为七天以后 UPDATE t_rebate_trade_item ...
- c++函数参数或返回值为函数指针
C++中函数指针的形式为:返回值类型 + 参数类型,函数没有值类型,但是却可以声明函数的指针,因为函数是可寻址的,存放在内存中的代码段,可以从指针访问. 函数指针可以声明为: void (*pF)(v ...
- 什么是shader?
一.什么是shader? shader是一段GLSL(openGL着色语言)小程序,运行在GPU(图形处理器),而非CPU使用GLSL语言编写,看上去像c或c++,但却是另外一种不同的语言.使用sha ...
- configerparser模块
'''[mysqld]charater-server-set='utf8'default-engine='innodb'skip-grant-table=Trueport=3306 [client]u ...
- Spring---异步消息
1.异步消息 1.1.目的: 为了 系统与系统 之间的通信: 1.2.概念: 异步消息 :消息的 发送者 无需 等待消息 接收者的处理及返回,甚至 无需 关心消息是否发送成功: 1. ...
- [POJ 1390] Blocking
问题描述 Some of you may have played a game called 'Blocks'. There are n blocks in a row, each box has a ...
- VM虚拟机中MAC OS调整磁盘大小
1.打开终端,输入diskutil list: 2.从显示的列表中找到你需要扩展的分区(是分区不是磁盘,分区的ID一般为diskXsX): 3.然后输入 diskutil resizeVolume d ...
- 02 IDEA创创建第一个maven工程
导入坐标