动态字符串(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)保存的内容,二进制不安全。
1、使用字符数组必须先给目标变量分配足够的空间,否则可能会溢出。
2、如果要获取字符长度,必须遍历字符数组,时间复杂度是O(n)。
3、C 字符串长度的变更会对字符数组做内存重分配。
4、通过从字符串开始到结尾碰到的第一个'\0'来标记字符串的结束,因此不能保存图片、音频、视频、压缩文件等二进制(bytes)保存的内容,二进制不安全。
立志如山 静心求实
浙公网安备 33010602011771号