当前位置:网站首页>Redis中SDS简单动态字符串
Redis中SDS简单动态字符串
2022-08-09 02:42:00 【FighterLiu】
前言
Redis的底层是使用c语言写的。但是Redis没有直接使用C语言传统的字符串表示(以空字符串结尾的数组,以下简称C字符串),而是自己构建了一种名为简单动态字符串(simple dynamic string,SDS)的类型,并将SDS用作Redis默认字符串表示。
一、SDS结构
SDS的定义如下:
struct sdshdr{
//记录buf数组中已使用字节的数量
//等于SDS所保存字符串的长度
int len;
//记录buf数组中未使用字节的数量
int free;
//字节数组,用于保存字符串
char buf[];
}
下图展示了SDS示例:
free 属性的值为0,表示这个SDS没有分配任何未使用空间。
len属性的值为5,表示这个SDS保存了一个五字节长的字符串。
buf属性是一个char类型的数组,数组的前5个字节分别保存了‘R’,‘e’,’’d,’i’,’s’五个字符,而最后一个字节则保存了空字符‘\0
SDS遵循C字符串以空字符串结尾的惯例,保存空字符的1字节空间不计算在SDS的len属性里面,并且为空字符分配额外的1字节空间,以及添加空间字符到字符串末尾等操作,都是由SDS函数自动完成的,所以这个空字符对于SDS的使用者来说是完全透明的。遵循空字符结尾这一惯例的好处是,SDS可以直接重用一部分C字符串函数库里面的函数。
二、SDS与C字符串的区别
常数复杂度获取字符串长度
因为C字符串不记录自身的长度信息,所以为了获取一个C字符串的长度,程序必须遍历整个字符串,对遇到的每个字符进行计数,直到遇到代表字符串结尾的空字符为止,这个操作的复杂度未O(N)。在SDS中记录了SDS本身的长度,所以获取一个SDS长度的复杂读仅为O(1).
杜绝缓冲区溢出
C字符串在做字符串内容拼接的时候,需要先分配足够的空间,否则会发生缓冲区溢出。比如下stract函数可以将src字符串的内容拼接到dest字符串的末尾:
char *stract(char *dest,const char *src);
假设程序里有两个在内存中紧邻着的C字符串s1和s2,其中s1保存了字符串“MongoDB”,如图所示
执行如下操作:
strcat(s1,”Cluster”);
在执行这个操作之前,没有给s1分配足够的空间,那么在stract函数之后,s1的数据将溢出到s2所在的空间中,导致s2保存的内容被意外的修改,如下:
与C字符串不同,SDS的空间分配策略完全杜绝了发生缓冲区溢出的可能性:当SDS API需要对SDS进行修改时,API会先检查SDS的空间是否满足修改所需的要求,如果不满足的话,API会自动将SDS的空间扩展至执行修改所需的大小,然后才执行实际的修改操作,所以使用SDS既不需要手动修改SDS空间大小,也不会出现前面所说的缓冲区溢出问题。
减少修改字符串是带来的内存分配次数
在C字符串中每次对其执行增长或缩短操作,程序总要对保存这个C字符串的数组进行一次内存重分配操作。因为内存重分配涉及复杂的算法,并且可能需要执行系统调用,所以它通常是一个比较耗时的操作。对性能也会产生一定的影响。
SDS中有一个free属性记录未使用空间,通过该属性实现了空间的预分配和惰性空间释放两种优化策略。
空间预分配
如果对SDS修改后,SDS长度(也即是len属性的值)将小于1MB,那么程序分配和len属性同样大小的未使用空间,这时SDS len属性的值将和free属性的值相同。例如如果修改之后,SDS的len将变成13字节,那么程序也会分配13字节的未使用空间,SDS的buf数组实际长度将变成13+13+1=27字节(额外的一字节用于保存空字符)。
如果对SDS进行修改之后,SDS的长度将大于等于1MB,那么程序会分配1MB的未使用空间。例如如果修改之后,SDS的len将变成30MB,那么程序会分配1MB的未使用空间,SDS的buf数组的实际场所为30MB+1MB+1Byte。
通过空间预分配策略,Redis可以减少连续执行字符串增长所需要的内存分配次数。因为在扩展SDS空间之前,SDS API会先检查未使用空间是否足够,如果足够的话,API就会直接使用未使用空间,而无需执行内存重分配。
惰性空间释放
惰性空间释放用于优化SDS字符串的缩短操作:当SDS的API需要缩短SDS保存的字符串时,程序并不立即使用内存分配来回收缩短后多出来的字节,而是使用free属性将这些字节的数量记录起来,并等到将来使用。
与此同时,SDS也提供了相应的API,让我们在有需要时,真正地释放SDS的未使用空间,所以不用担心惰性空间释放测试会照成内存浪费。
二进制安全
C字符串中的字符必须符合某种编码(比如ASCII),并且除了字符串的末尾之外,字符串里面不能包含空字符,否则最先被程序读入的空字符将被误认为是字符串结尾,这些限制使的C字符串只能保存文本数据,而不能保存像图片,音频,视频,压缩文件这样的二进制数据。
SDS不会对数据做任何限制,数据在写入时是什么样的,它被读取的时候就是什么样。
边栏推荐
猜你喜欢
高性能 MySQL(十二):分区表
Postman接口测试【官网】最新版本 安装及使用入门教程
(面试题)面试官为啥总是让我们手撕call、apply、bind?
2022年最流行的自动化测试工具有哪些?全网最全最细都在这里了
搭建Eureka注册中心集群 ,实现负载均衡
Pytest+request+Allure实现接口自动化框架
online schema change and create index
普通人如何增加收入
"Lonely Walking on the Moon": Two choices of Duguyue, let a "middleman" become a big hero
Jenkins configuration nail notification
随机推荐
ZCMU--5115: Buying Keys(C语言)
高性能 MySQL(十二):分区表
独立机器连接cdh的spark集群,远程提交任务(绝对可以成功,亲测了n遍)
20220524搜索和排序:搜索二维矩阵II
全志平台双路LVDS配置
OJ:L2-012 关于堆的判断
Postman接口测试【官网】最新版本 安装及使用入门教程
全文翻译:Multimodal Neural Networks: RGB-D for Segmantic Segmentation and Object Detection
中国SSD产业突围有多难?除了技术“瓶颈”还有哪里挑战?
Open3D 点云曲率计算
What are the most popular automated testing tools in 2022?The most complete and most detailed of the entire network is here
Solve the Final Fantasy 13-2 Clock Puzzle with DFS
【无标题】
终于有人把灰度发布架构设计讲明白了
搭建Eureka注册中心集群 ,实现负载均衡
(面试题)面试官为啥总是让我们手撕call、apply、bind?
并查集相关知识点
Redis系列文章导航
Likou Brush Question Record 1.5-----367. Valid perfect squares
最强分布式锁工具:Redisson