动态字符串(Simple Dynamic Stirng)

  SDS基本结构如下:

struct sdshdr {
  int len; // 记录buf数组中已使用字节的数量,等于SDS所保存字符串的长度 
  int free; // 记录buf数组中未使用字节的数量 
  char buf[];// 字节数组,用于保存字符串 
};

    SDS的优势

1、O(1)时间复杂度获取字符串长度

  SDS内部维护着一个字符串长度的len变量,可以直接读取,时间复杂度为O(1)。对于传统的C字符串:字符+“\0”,想要获取字符长度,则需要遍历整个字符串,直到遇到结束字符,时间复杂度为O(n)。

2、缓冲区溢出规避

  所谓缓冲区溢出即所需要的内存超出了实际的内存。对于C字符串来说,要特别注意内存分配,回收使用问题。比如,向一个现有字符串内添加特定字符时,需要保证当前已经分配了这足够的内存。 

SDS的空间预分配策略可以避免缓冲区溢出发生,当需要对SDS进行操作时,首先会检查当前空间是否满足需求,不足则扩展当前分配空间。

3、减少内存重分配次数

  C字符操作前都需要进行内存的分配操作,同时,操作完成后,也需要进行相应的内存回收操作。一次操作至少涉及一次内存分配操作。Redis 在SDS内存配置策略上采用了空间预分配+惰性删除相结合的策略。预分配,也即是说在一次扩展操作中,扩展的空间大小会大于实际需要的空间大小。预分配,也即是说在一次扩展操作中,扩展的空间大小会大于实际需要的空间大小。

 4、二进制安全

      SDS相关的功能方法会以二进制的形式来操作SDS存储的数据,没有任何中间操作,存储最原始的数据,因此不会有字符层面的因素影响。SDS可以保存任何源的二进制数据,字符、图片、文件或者序列化的对象等等。

  SDS 又有多种结构(sds.h):sdshdr5、sdshdr8、sdshdr16、sdshdr32、sdshdr64,用于存储不同的长度的字符串,分别代表

  2^5=32byte,
  2^8=256byte,
  2^16=65536byte=64KB,
  2^32byte=4GB。

    数据结构定义:

/* sds.h */
struct __attribute__ ((__packed__)) sdshdr8 {
    uint8_t len; /* 当前字符数组的长度*/
    uint8_t alloc; /*当前字符数组总共分配的内存大小*/
    unsigned char flags; /* 当前字符数组的属性、用来标识到底是sdshdr8 还是sdshdr16 等*/
    char buf[]; /* 字符串真正的值*/
};

  为什么Redis 要用SDS 实现字符串?

  C 语言本身没有字符串类型(只能用字符数组char[]实现)。
  1、使用字符数组必须先给目标变量分配足够的空间,否则可能会溢出。
  2、如果要获取字符长度,必须遍历字符数组,时间复杂度是O(n)。
  3、C 字符串长度的变更会对字符数组做内存重分配。
  4、通过从字符串开始到结尾碰到的第一个'\0'来标记字符串的结束,因此不能保存图片、音频、视频、压缩文件等二进制(bytes)保存的内容,二进制不安全。
  
  

  

posted on 2021-07-26 16:58  溪水静幽  阅读(174)  评论(0)    收藏  举报