Redis数据结构-简单动态字符串(SDS)

1. 简介

Redis没有使用C中默认的字符串(字符数组),而是自己构建了一个简单动态字符串(SDS)的抽象类型,并且作为默认的字符串。

  • Redis中会使用C中的字符串作为字符常量,用在无需修改的地方,如日志;
  • 当字符串会被修改时,就是使用SDS;
  • 此外,SDS还会被用作缓冲区。

2. 定义

sds.h/sdshdr 结构体表示一个SDS值:

struct sdshdr {
    unsigned int len;
    unsigned int free;
    char buf[];
};

相比普通的C字符串,SDS的优点在于:

  • 能够通过len字段方便的知道字符串长度;
  • 通过free字段能方便知道缓存区剩余空间;
  • 同样字符串buf以'\0'结尾,能够与print等C中函数接口通用;
  • 杜绝缓冲区溢出(SDS会先进行检查,空间不够会进行扩展)。
  • 二进制安全。
    在使用SDS API的情况下,不要手动修改len和free字段,它们的维护是API内部自动进行的。

3. 内存优化策略

3.1 空间预分配

在使用SDS API进行字符串修改需要扩容时,不仅会分配修改所必须的空间,而且还会分配额外的未使用空间。总体策略:

  • 如果修改后的SDS长度(len字段)小于1MB,那么程序会分配同len相同的未使用空间,即free=len,实际长度是free+len+1=2len+1;
  • 如果修改后的SDS长度(len字段)大于1MB,那么程序会分配1MB的未使用空间。

通过空间预分配,可以减少内存分配次数。

3.2 惰性空间释放

释放策略是:弹药使用API缩短SDS字符串时,程序并不会回收多出来的字节,而是使用free字段记录下来,以等待将来被使用。
通过惰性空间释放,可以减少内存回收的次数,未回收的内存可以继续用于字符串的存储。
如果要回收内存,SDS提供了相应的API。

4. 特点

4.1 二进制安全

虽然SDS的buf保存了C的\0结尾的约定,但是它实际内容其实是由len字段决定,因此存储音视频等二进制数据是没有问题的,不用对数进行任何过滤操作。

SDS也因此被称为字节数组--保存二进制数据。

4.2 兼容部分C函数

SDS仍然遵循C字符串以空白字符结尾的惯例,因此它也能使用C字符串中<string.h>部分函数。
如stacat、strcmp等。

区别:

  1. C字符串只能保存文本数据,而SDS可以二进制数据;
  2. C字符串API操作可能会导致缓冲区溢出,而SDS不会;
  3. SDS只能使用部分C函数;
  4. SDS获取字符串长度的时间复杂是O(1),而普通C字符串是O(n);
  5. 空间扩展,SDS会进行预分配,实际分配次数会比C字符串少。
posted @ 2020-04-17 13:26  星星,风,阳光  阅读(308)  评论(0编辑  收藏  举报