一,什么是SDS?
1.引出SDS
C字符串:c语言中,用空字符结尾的字符数组表示字符串
简单动态字符串(SDS):Redis中,用SDS来表示字符串。在Redis中,包含字符串值的键值对在底层都是由SDS实现的
首先,Redis使用C语言写的,但是Redis没有使用C语言传统的字符串表示,它自己构建了简单字符串的抽象类型来表示字符串。
2.SDS的定义
一个sdshdr结构表示一个SDS值。结构如下:
struct sdshdr {
//记录buf数组中已使用字节的数量
int len;
//记录buf数组中未使用字节的数量
int free;
//char类型的数组,用来保存字符串
char buf[];
}
注意:SDS还是遵循了C字符串以空字符结尾的惯例,最后一个字节保存空字符'\0'。这样做的好处是SDS可以重用一些C字符串函数库里面的函数。
3.SDS和C字符串的区别
A.结构上
B.获取字符串的长度时:
C字符串需要遍历整个字符串,当发现空字符时,停止计数。时间复杂度为O(n)。
SDS中的len属性记录了SDS的长度。时间复杂度为O(1)。
C.杜绝缓冲区溢出:
由于C字符串不记录自身的长度。当修改字符串之前,如果没有为字符串分配(我们手动分配)足够的空间,会造成缓冲区溢出
当修改SDS字符串时,API会先检查SDS的空间是否满足修改所需要的要求(通过 len free 来判断)。如果不满足,API会自动(不需要我们手动分配)修改SDS空间的大小
D.减少内存重新分配次数
对于C字符串来说,每次修改时,都需要对保存这个C字符串的数组进行一次内存重新分配操作。
如果拼接字符串,在拼接之前,程序需要先通过内存重新分配扩展底层数组的空间大小。否则会缓冲区溢出
如果缩短字符串,在截断字符串之后,程序需要通过内存重新分配来释放不用的空间。否则会内存泄漏
对于SDS来说,通过未使用空间 len free,实现了空间预分配和惰性空间释放两种优化策略。
空间预分配(字符串增长):修改之后,如果SDS的长度小于1MB,程序分配和len同样大小的未使用(len free)
空间。如果大于1MB的空间,程序分配1MB的未使用空间。
惰性空间释放(字符串缩短):缩短字符串后,程序不是立即使用内存重新分配来回收缩短后多出来的字节,
而是使用free属性将这些字节的数量记录起来,等待将来使用。
E.二进制安全
只关心二进制化的字符串,不关心具体格式,只会严格按照二进制的数据存取,不会根据某种特殊的标志来解析。
C字符串中,空字符串被当做字符串的结尾来解析的。比如存hello world字符串时,C字符串只会保存hello。因为对于中间的空格,C字符串解析成空字符,当做这个字符串的结尾。world就丢失了。但是SDS字符串,写入是什么样,它被读取就是什么样。那么SDS是如何判断字符串是否结束呢?通过len属性的值获取字符串的长度,字符串长度是多少,在读取字符串的时候就读取多少长度。
F.兼容部分C字符串函数
通过遵循C字符串以空字符结尾的惯例,SDS可以在有需要的时候重用<String.h>函数库