简单动态字符串 — SDS
1. SDS类型定义
Redis底层字符结构没有沿用C语言中的字符,从新定义了一种 SDS ( simple dynamic string )的抽象类型。
struct sdshdr {
// 计入buf字符的长度
int len;
// 记录buf中空闲长度
int free;
// 字节组,
char buf[]
}
2. SDS 特点
遵循C 语言习惯,每个字符后使用’\0’结尾,最后一个空字符不计入 len 中。则SDS的buf字段 可以直接使用C语言函数库
3. SDS 优势
- 降低长度获取复杂度
- 杜绝缓冲区溢出
- 减少字符串修改导致内存重新分配次数
- 二进制安全
- 兼容 C 字符串函数库(<String.h>)
3.1 降低长度获取复杂度
C语言判断字符串长度,需要遍历存储所有字符,累加得到结果,复杂度为O(N),SDS 直接通过Len字段可以获取长度,复杂度为O(1)。
3.2. 杜绝缓冲区溢出
C 语言在字符串 拼接时候,需要提前分配好空间,不然会因为内存不足,发生溢出或错误修改。SDS则会提前判断大小,内存不足会进行扩容。再拼接。
3.3 减少字符串修改导致内存重新分配的次数
内存重新分配设计复杂的算法,比较耗时,程序修改字符串的情况不太常出现,同时Redis作为高性能数据库,频繁发生重新分配内存会对性能造成影响是不可取的。Redis解决这个问题是通过空间预分配和惰性空间释放。
- 空间预分配
当 SDS 字符串发生修改,并且需要 SDS 的需要扩容的时候,程序不仅会分配必要空间,还会额外对其分配未使用空间。额外分配未使用空间有两种方式:
- SDS 长度 < 1MB, 分配与 SDS 等长空间
- SDS 长度 >= 1MB, 分配 1MB 空间
- 惰性空间释放
当 SDS 字符串发生缩短操作,SDS 不会立刻将缩短后多出的字节释放,而是由 free 字段记录,当 SDS 发生修改需要时,添加的内容小于 free 字节长度,则无需重新分配空间。此外,我们无需担心内存泄露,SDS 提供 API 可以真正释放未使用空间。
3.4 二进制安全
C 语言的字串因为编码问题,最后一个字符为空字符(‘\0’),不允许有其他字符,否则其他字符会被忽略,无法读取。因此受限,无法保存 图片,音频,压缩文件等二进制数据。SDS 结构中的 buf 保存的都是二进制数据,未对保存内容做任何的假设与限制。因此,SDS 保存前保存后一致。SDS 是通过 len 字段读取内容。保证完整读取。
3.5 兼容部分C字符串函数
因为SDS 遵守 C 语言字符末尾使用空字符(‘\0’)结尾,如此我们可以直接使用 C 字符串函数库。