定义:简单动态字符串(SDS)
Redis虽然是用C语言开发的。但Redis的字符串并非C语言的字符串。而是其创建了一种名为简单动态字符串的抽象类型(SDS)。并将SDS作为Redis的默认字符串表示。
SDS用于何处
Redis只会使用C字符串作为自变量。在大多数情况下,Redis使用SDS作为字符串表示。
比如Redis存储字符串键值对,
set msg "hello world"
此时键与值都是SDS类型;
存储列表类型
rpush a "a1" "a2"
键是SDS类型,值是list,list存放的字符串也是SDS类型。
SDS优点
比起C字符串,SDS具有以下优点:
- 常数复杂度获取字符串长度。
- 杜绝缓冲区溢出。
- 减少修改字符串长度时所需的内存重分配次数。
- 二进制安全。
- 对兼容部分C字符串函数。
SDS结构
struct sdshdr {
//记录buf数组中已使用字节的数量
//等于SDS所保存字符串的长度
int len;
//记录buf数组中未使用字节的数量
int free;
//字节数组,用于保存字符串
char buf[];
};
优点详解
常数复杂度获取字符串长度
定义变量len,当想要获取SDS长度时即可直接返回,无需遍历字符串数组。
杜绝缓冲区溢出
C字符串不记录自身的长度,在执行字符串拼接的时候,若原字符串的内存长度不足以支持拼接时,就会溢出到相邻的内存空间中,造成别的数据内容的修改。 而SDS的API在进行修改时,会检查SDS剩余空间是否足够,若不足够会进行扩容。然后才会执行实际的修改操作。所以不会产生缓冲区溢出问题。
减少修改字符串长度时所需的内存重分配次数
对于包含N个字符的C字符串来说,底层实现是一个N+1的数组。比如存储REDIS的话,会存成REDIS\0。\0代表一个空位,C字符串的结尾都是\0。
因此当修改字符串长度的时候,都需要进行内存的重新分配。比如需要扩展数组的空间或者释放掉部分空间。内存重分配是一个复杂的算法可能需要执行系统调用,因此比较耗时。
- 空间预分配
SDS由于里面free代表未使用的空间数量,当字符串长度添加时,若free足够使用,就不会进行分配内存。
- 惰性删除
当SDS空间需要缩短时,程序不是立即进行内存重分配回收多余的字节,而是使用free属性将这些字节数量记录下来,并等待将来使用。并可以在有需要是,真正地释放SDS的未使用空间,不必担心内存浪费。
注:SDS字符串的最大容量是512MB,当扩容时不到1MB,扩容时会分配原先一倍的长度,当扩容时超过1MB是,只会再增加1MB
二进制安全
C字符串的中间不能有空格,比如保存Redis Redis,就只能变成Redis了。而二进制数据是存在空格的,所以是没法保存的,而SDS则是可以正常保存的。
对兼容部分C字符串函数
redis可以兼容一部分的C语言的字符串函数。