redis底层数据结构之简单动态字符串(SDS)

简单动态字符串(simple dynamic string,SDS)

redis使用C语言编写的,但是redis的字符串却不是C语言中的字符串(以空字符'\0'结尾的字符数组),redis定义了一种简单动态字符串(simple dynamic string,SDS)的抽象类型,并将SDS作为redis的默认字符串


1 SDS结构

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

其中:

len:记录buf数组中已使用字节的长度,等于buf[]中字符串的长度
free:记录buf数组中未使用字节的长度
buf[]:字节数组,用于保存字符串

 

2 SDS结构示意图

encoding = REDIS_ENCODING_INT 或 REDIS_ENCODING_EMBSTR  或  REDIS_ENCODING_RAW

分配了10Byte空间,占用了6Byte,未使用4Byte

保存空字符的1Byte不包含在len属性里,redis默认为空字符分配额外的1Byte,并把空字符添加到字符串末尾,这些操作都由SDS函数自动完成

示意图如下:

 

 

3 redis使用SDS的优点

1) 常数复杂度获取字符串长度
   获取SDS字符串的长度只需要读取len属性,时间复杂度为O(1);而C语言中获取字符串的长度通常是经过遍历计数来实现的,时间复杂度为O(n)

2) 杜绝缓冲区溢出
    C语言中使用strcat函数来进行字符串的拼接,一旦没有分配足够的内存空间,就会造成缓冲区溢出
   而SDS在修改字符串时,首先根据len属性检查内存空间是否满足需求,如果不满足,会进行空间扩展然后在修改,所以不会出现缓冲区溢出

3) 减少修改字符串的内存重新分配次数
  C语言不记录字符串的长度,修改字符串时必须要重新分配内存(先释放再申请),如果不重新分配内存,字符串长度增大时会造成内存缓冲区溢出,字符串长度减小时会造成内存泄露
  而SDS修改字符串时使用空间预分配和惰性空间释放两种策略:
  a) 空间预分配:对字符串进行空间扩展时,扩展的内存比实际需要的多(修改后的字符串小于1MB时,那么会分配与len属性相同大小的未使用空间;大于1MB时,会分配1MB的未使用空间),这样可以减少连续执行字符串增长操作所需的内存重分配次数
  b) 惰性空间释放:对字符串进行缩短操作时,不会立即回收缩短后剩余的内存空间,而是使用free属性将剩余的内存空间记录下来,等待后续使用(当有需要时,也可以手动释放这些未使用的空间)
 
4) 二进制安全
  C字符串是以空字符作为字符串结束的标识,而二进制文件(如图片等)内容可能包括空字符串,因此C字符串无法正确存取
  而SDS不是以空字符作为字符串结束的标识,而是以len属性表示的长度来判断字符串是否结束
 
5) 兼容部分C字符串函数
  虽然SDS是二进制安全的,但是一样遵从每个字符串都是以空字符串结尾的惯例,这样可以重用C语言库<string.h>中的一部分函数

 

posted @ 2022-12-17 18:04  junffzhou  阅读(85)  评论(0编辑  收藏  举报