【Redis数据结构】简单动态字符串SDS

90 阅读3分钟

定义:简单动态字符串(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具有以下优点:

  1. 常数复杂度获取字符串长度。
  2. 杜绝缓冲区溢出。
  3. 减少修改字符串长度时所需的内存重分配次数。
  4. 二进制安全。
  5. 对兼容部分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。

因此当修改字符串长度的时候,都需要进行内存的重新分配。比如需要扩展数组的空间或者释放掉部分空间。内存重分配是一个复杂的算法可能需要执行系统调用,因此比较耗时。

  1. 空间预分配

SDS由于里面free代表未使用的空间数量,当字符串长度添加时,若free足够使用,就不会进行分配内存。

  1. 惰性删除

当SDS空间需要缩短时,程序不是立即进行内存重分配回收多余的字节,而是使用free属性将这些字节数量记录下来,并等待将来使用。并可以在有需要是,真正地释放SDS的未使用空间,不必担心内存浪费。

注:SDS字符串的最大容量是512MB,当扩容时不到1MB,扩容时会分配原先一倍的长度,当扩容时超过1MB是,只会再增加1MB

二进制安全

C字符串的中间不能有空格,比如保存Redis Redis,就只能变成Redis了。而二进制数据是存在空格的,所以是没法保存的,而SDS则是可以正常保存的。

对兼容部分C字符串函数

redis可以兼容一部分的C语言的字符串函数。