惊呆了!Redis中的字符串竟然不是C字符串!!!

SDS简介

在这里插入图片描述
Redis是用C语言写的,但是他的字符串竟然不是C字符串,这让我很惊讶。既然不是C字符串,那一定有一个数据类型吧,这个数据类型是简单动态字符串(Simple Dynamic String,SDS),这是什么鬼,第一次听说。既然用他,他肯定有厉害的地方,接下来就研究研究他厉害到哪!
先举个例子说说字符串。

127.0.0.1:6379> set msg "hello"
OK
127.0.0.1:6379> keys *
1) "msg"

上面的"msg"和"hello"就是字符串,而且是一个SDS字符串,可不是一个C字符串。

SDS的内部存储结构

好了,说了这么多,还没介绍啥是SDS呢,先说一下SDS的内部存储结构,其中一个SDS,内部包含三部分

char buf[];// 字节数组,用于保存字符串
int len; // buf数组中已使用的字节数量,即是SDS字符串的长度
int free;// buf数组中未使用的字节数量

C字符串的内部存储结构

char str[];// 其内部就是一个简单的字节数组

SDS和C字符串的区别

获取字符串长度
由于C字符串没有哦记录自身的长度信息,所以获取C字符串长度的时候,必须遍历整个字符串,其时间复杂度是O(n),而SDS中有len属性,所以在获取其长度时,时间复杂度为O(1)。
缓冲区溢出
由于C字符串不记录字符串的长度,当我们进行字符串拼接的时候,可能会出现缓冲区溢出问题,举个例子,我们有s1和s2两个字符串,当我们想要把s2字符串拼接到s1字符串的时候,如果在拼接之前没有给s1分配足够的空间,那么s1的数据就会溢出到s2所在的空间,导致s2中原有的数据被破坏。而SDS在进行修改操作时,会先检查空间是否够用,如果不够用会自动扩展,以解决缓冲区溢出的问题。
内存重分配次数
对于C字符串而言,当对字符串进行拼接操作时,需要先通过内存重分配来扩展其底层的数组空间大小,如果不扩展,很可能就会出现缓冲区溢出,当金星字符串缩短时,也需要进行内存重分配,进行释放那部分不用的内存,如果忘了这步,会产生内存泄漏。可以看出,不管是字符串拼接,还是字符串缩短,都要执行内存重分配。
SDS就不一样了,SDS在进行字符串拼接的时候,采用空间预分配,举个例子进行讲解,如果我们有s1="hello"和s2=“sds”,两个字符串,以s1为研究对象,s1的len为5,free为0,当s2拼接到s1时,进行内存重分配,s1的len变成了8,free也变为8,这就是空间预分配,当你下次进行拼接"world"字符串的时候,由于free的空间够用,就可以直接分配了,而不用再次内存重分配。当我们进行缩短字符串的时候,SDS采用惰性空间释放,如果我们有一个字符串为s=“hellosds”,其len为8,free为0,当我们把sds进行截取掉的时候,其len变为了5,free变为了3,也就是这三个空间并没有通过内存重分配释放。这样就减少了内存重分配的次数,同时提高了效率,需要注意的是,free空间并不是无限制的大,free空间上限是1MB
二进制安全
C字符串的编码是ASCII编码,在字符串的末尾是以\0结束,也就是空字符,所以在字符串中不能包含空字符,要不然会让程序误以为结束,这也限制了C字符串只能保存文本数据,不能保存图片,音频,视频等二进制数据。
SDS不仅可以保存文本数据,还可以以二进制方式把其他数据存储到buf数组中的,程序不会对数据做任何限制,写入什么样,读取就是什么样,这样不仅可以存储文本,而且可以存储任意格式的二进制数据。
SDS兼容部分C字符串函数
SDS虽然是二进制安全的,但是他同样遵循C字符串以空字符串解为的惯例,所以SDS可以重用C字符串的一些函数。

总结

SDS获取字符串长度的时间复杂度是O(1),C字符串是O(n)。
SDS不会造成缓冲区溢出,C字符串可能会。
SDS修改N次字符串长度最多执行N次内存重分配,C字符串是执行N次。
SDS可以保存文本数据和二进制数据,C字符串只能保存文本数据。
SDS可以使用部分<string.h>库函数,C字符串可以使用全部。
总之SDS在Redis中很强。

原创文章 483 获赞 395 访问量 10万+

猜你喜欢

转载自blog.csdn.net/HeZhiYing_/article/details/105737316