redis 系列3 数据结构之简单动态字符串 SDS
一. SDS概述
Redis 没有直接使用C语言传统的字符串表示,而是自己构建了一种名为简单动态字符串(simple dynamic string, SDS)的抽象类型,并将SDS用作Redis的默认字符串表示。Redis只会使用C字符串作为字面量。在Redis里,使用SDS来表示字符串值,是一个可以被修改的字符串,字符串“键值对”底层都是由SDS实现的。
-- 例1:客户端执行如下命令:
127.0.0.1:> set msg "hello world"
OK
127.0.0.1:> get msg
"hello world"
上面例1中就在数据库里创建一个新的键值对。 其中“键”是一个字符串对象,对象的底层实现是一个保存着字符串"msg" 的SDS。 "键值" 也是一个字符串对象,对象的底层实现是一个保存着字符串" hello world " 的SDS。
-- 例2: 客户端执行如下命令:
127.0.0.1:> rpush fruits "apple" "banana" "cherry"
(integer)
127.0.0.1:> lrange fruits -
) "apple"
) "banana"
) "cherry"
上面例2中也在数据库里创建一个新的键值对。其中“键”是一个字符串对象,对象的底层实现是一个保存着字符串" fruits " 的SDS。"键值"是一个列表对象,列表包含了三个字符串对象,分别由三个SDS实现。
SDS除了用来保存数据库中的字符串值之外,还用作缓冲区(buffer): AOF模块中的AOF缓冲区,以及客户端状态中的输入缓冲区。
二. SDS 定义
每个SDS.h文件下的sdshdr结构表示一个SDS值, 下面是Redis源码,在github的地址是https://github.com/antirez/sds
struct sdshdr{
//记录buf数组中已使用字节的数量,也就是字符串的长度
int len ;
// 记录buf数组中未使用字节的数量
int free;
//字节数组,用于保存字符串
char buf[];
}
在C语言中使用长度为N+1的字符数组来表示长度为N的字符串,并且字符数组最后一个元素总是空字符'\0'。 假设SDS的值为 "Redis",那么free属性值为0, len 属性值为5, buf数组为R,e,d,i,s五个字符,最后一个字节则保存空字符'\0' 。
三. SDS与C字符串的区别
C语言使用简单的字符串表示方式,并不能满足Redis对字符串在安全性,效率以及功能方面的要求,从几个方面说明:
1. 常数复杂度获取字符串长度
因为c字符串并不记录自身的长度信息,所以为了获取一个c字符串的长度,程序必须遍历整个字符串。与C 字符串不同,因为SDS在len属性中记录了SDS本身的长度,对于SDS的值为"Redis"的字节长度就是5。
2. 杜绝缓冲区溢出
在c中, 假设紧邻的字符串s1 和 s2, s1保存为"redis", s2保存为"mongodb", 如果修改s1的值为 redis cluster, 但修改之前没了空间,那么s1的数据将溢出到s2所在空间中。
在SDS中,会先检查给定的SDS空间是否足够,会自动扩展修改所需的大小空间。然后在执行实际的修改操作。
3. 减少修改字符串时带来的内存重分配次数
在c 中,字符串的底层实现总是一个N+1个字符长的数组,因为字符串的长度和底层数组的长度之间存在着这种关联,所以每次增长或者缩短一个c字符串,程序都要对保存这个C字符串的数组进行一次内存重分配操作。
在SDS中通过未使用空间解除了字符串长度和底层数组长度之间的关联,buf数组的长度不一定就是字符数量加1, 数组里机可以包含未使用的字节,这些由free属性记录。
3.1 空间预分配
当SDS的API对一个SDS进行操作,并且需要对SDS进行空间扩展的时候,程序不仅会为SDS 分配修改所必须要的空间,还会为SDS分配额外的未使用空间。额外分配的未使用空间数量由以下公式决定:
如果对SDS进行修改之后,SDS的长度(也即是len属性的值) 将小于1MB,那么程序分配和len属性同样大小的未使用空间。这时SDS len属性的值将和fee属性的值相同,例如:修改之后,SDS的len将变成13字节, 那么程序也会分配13字节的未使用空间,SDS的buf数组的实际长度将变成13+13+1=27字节。
如果对SDS进行修改之后,SDS的len大于等于1MB, 那么程序会分配1MB的未使用空间,如果对SDS进行修改之后, SDS的len变成30MB,那么程序会分配1MB的未使用空间,SDS的buf数组的实际长度为30MB + 1MB +1byte。
通过空间预分配策略,Redis可以减少连续执行字符串增长操作所需的内存重分配次数。
3.2 惰性空间释放
惰性空间释放用于优化SDS的字符串缩短操作。当SDS的API需要缩短SDS保存的字符串时,程序并不立即使用内存重分配来回收缩短多出来的字节,而是使用free属性将这些字节的数据记录起来,并等待将来使用(缩短后未使用的空间不会释放,而是将来增长操作时,再使用这些未使用空间)。
4. 二进制安全
在c字符串的字符必须符合某种编码(如ASCII), 并且除了字符串的末尾之处,字符串里面不能包含空字符,否则最先被程序读入的空字符将被误以为是字符串的结尾,这使得c字符串只能保存文本数据,不能保存图片,音频,视频,压缩文件之类的二进制数据。
为了保证Redis 可以适用各种不同的使用场景,SDS的API 都是二进制安全的,程序不会对其中的数据做任何限制,过滤,数据写入是什么,读取时就是什么。
5. 兼容部分C字符串函数
在SDS中会遵循C字符串以空字符结尾的惯例,总会为buf数组分配空间时多分配一个字节来容纳这个空字符,这是为了让SDS的字符串可以重用一部分(string.h>库定入的函数。
四 总结
4.1 C字符串与SDS之间的区别总结
C字符串 |
SDS |
获取字符串长度的复杂度为0(N) |
获取字符串长度的复杂度为0(1) |
API是不安全的,可能会造成缓冲区溢出 |
API是安全的,不会造成缓冲区溢出 |
修改字符串长度N次必然需要执行N次内存重分配 |
修改字符串长度N次最多需要执行N次内存重分配 |
只能保存文本数据 |
可以保存文本或者二进制数据 |
可以使用所有<string.h>库中函数 |
可以使用一部分<string.h>库中函数 |
4.2 SDS API(主要的一些API)
函数 |
作用 |
sdsnew |
创建一个SDS字符串 |
sdsempty |
创建一个不包含内容的空SDS 字符串 |
sdsfree |
释放给定的SDS字符串未使用空间 |
sdslen |
返回SDS字符串已使用空间字节数 |
sdsavail |
返回SDS字符串未使用空间字节数 |
sdsdup |
创建一个给定SDS的副本 |
sdsclear |
清空SDS字符串内容 |
sdscat |
将给定c字符拼接到SDS字符串的末尾 |
sdscatsds |
将给定的SDS字符串拼接到另一个SDS字符串的末尾 |
sdscpy |
将给定的c字符拼复制到SDS里机,覆盖SDS原有字符串 |
sdsgrowzero |
用空字符将SDS扩展至指定长度 |
sdsrange |
保留SDS指定区间内的数据,不在区间内的数据会被覆盖或清除 |
sdstrim |
接受一个SDS和一个C字符串作为参数,从SDS中移除所有在C字符串中出现过的字符 |
sdscmp |
对比两个SDS字符串是否相同 |
redis 系列3 数据结构之简单动态字符串 SDS的更多相关文章
- Redis数据结构之简单动态字符串SDS
Redis的底层数据结构非常多,其中包括SDS.ZipList.SkipList.LinkedList.HashTable.Intset等.如果你对Redis的理解还只停留在get.set的水平的话, ...
- Redis 数据结构之简单动态字符串SDS
几个概念1:key对象 数据库存储键值对的键,总是一个字符串对象.2:value对象 数据库存储键值对的值,可以是字符串对象,list对象,hash对象,set对象,sorted set对象. ...
- 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
图解Redis之数据结构篇--简单动态字符串SDS 前言 相信用过Redis的人都知道,Redis提供了一个逻辑上的对象系统构建了一个键值对数据库以供客户端用户使用.这个对象系统包括字符串对象 ...
- Redis底层探秘(一):简单动态字符串(SDS)
redis是我们使用非常多的一种缓存技术,他的性能极高,读的速度是110000次/s,写的速度是81000次/s.这么高的性能背后,到底是怎么样的实现在支撑,这个系列的文章,我们一起去看看. redi ...
- 【redis】redis底层数据结构原理--简单动态字符串 链表 字典 跳跃表 整数集合 压缩列表等
redis有五种数据类型string.list.hash.set.zset(字符串.哈希.列表.集合.有序集合)并且自实现了简单动态字符串.双端链表.字典.压缩列表.整数集合.跳跃表等数据结构.red ...
- 深入理解Redis 数据结构—简单动态字符串sds
Redis是用ANSI C语言编写的,它是一个高性能的key-value数据库,它可以作用在数据库.缓存和消息中间件.其中 Redis 键值对中的键都是 string 类型,而键值对中的值也是有 st ...
- Redis数据结构之简单动态字符串
Redis没有直接使用C语言传统的字符串表示(以空字符结尾的字符数组), 而是自己构建了一种名为简单动态字符串(simple dynamic string,SDS)的抽象类型, 并将SDS用作Redi ...
- 【Redis】简单动态字符串SDS
C语言字符串 char *str = "redis"; // 可以不显式的添加\0,由编译器添加 char *str = "redis\0"; // 也可以添加 ...
随机推荐
- reactjs 学习笔记
1.安装 npm install -g create-react-app create-react-app my-app cd my-app npm start
- 20175229张智敏 Arrays和String单元测试
Arrays和String单元测试 1.具体要求: 在IDEA中以TDD的方式对String类和Arrays类进行学习 测试相关方法的正常,错误和边界情况 String类 charAt split A ...
- 第48章:MongoDB-备份和恢复
①文件系统快照(snapshot) 生成文件系统快照这个方法需要满足两个条件: 1:文件系统本身支持快照技术 2:运行mongod时必须开启日记系统 其恢复就是快照的恢复,当然,恢复的时候,确保mon ...
- Object constraint language for code generation from activity models
一.基本信息 标题:Object Constraint Language for Code Generation from Activity Models 时间:2018 出版源:Informatio ...
- 使用rsync实现不同Linux服务器间目录同步
实现目标: A 服务器上 /opt/web 目录,与B服务器上 /opt/web目录实现同步.即:B主动与A进行同步. OS: Reaht AS4 A Server 192.168.1 ...
- impala教学视频
https://www.iqiyi.com/playlist394935102.html
- JAVA对象和类
Java作为一种面向对象语言.支持以下基本概念: 多态 继承 封装 抽象 类 对象 实例 方法 重载 Java中的类 类可以看成是创建Java对象的模板. 通过下面一个简单的类来理解下Java中类的定 ...
- MFC对话框和属性表
对话框主要有两类,分为模式和无模式,它们的功能都被封装在了CDialog中.与对话框相近的是属性表,属性表实际上是具有制表页(TAB 页)的对话框,将对话框中控件更好的组织在一起.借助Cpropert ...
- 64位平台C/C++容易犯的错误
64位平台的介绍 IA-64 is a 64-bit microprocessor architecture developed by Intel and Hewlett Packard compa ...
- Idea环境下git 图形化操作
大家好,之前我们介绍了<IDEA环境下GIT操作浅析之一Idea下仓库初始化与文件提交涉及到的基本命令>和<IDEA环境下GIT操作浅析之二-idea下分支操作相关命令>,本文 ...