Redis 的简单动态字符串(Simple Dynamic String, SDS)是 Redis 数据库内部用于存储字符串的一种自定义数据结构,它是 Redis 对传统 C 语言字符串(以空字符 ‘\0’ 结尾的字符数组)的一种增强版本,旨在解决原生 C 字符串在实际使用中遇到的动态增长、性能瓶颈和二进制安全等问题。下面是一些关于 SDS 关键特性和源码解析要点:
SDS 结构定义
在 Redis 源码中,SDS 结构通常定义如下:
typedef char *sds;
struct sdshdr {
// 记录已占用空间(包括结束符'\0')
int len;
// 记录未使用的预留空间大小
int free;
// 字节数组,用于存储字符串内容
char buf[];
};
实际上,SDS 是通过 sdshdr
结构体来包装原始的 C 字符数组 buf
,并在其前增加两个额外的字段 len
和 free
,分别记录已经使用的字符数量(不含结束符)和剩余可用空间大小。
SDS 主要特点
-
O(1) 时间复杂度获取字符串长度:由于 SDS 内部维护了已使用的长度信息
len
,所以获取字符串长度不再需要遍历整个字符串,时间复杂度降为 O(1)。 -
空间预分配:SDS 在执行字符串增长操作时,不仅分配当前所需的空间,还会根据配置预先分配额外的未使用空间,从而减少连续增长时的频繁内存重分配。
-
惰性释放:当缩短字符串时,不会立即回收多出来的空间,而是将这部分空间记入
free
字段,以便后续可能的增长操作复用。 -
二进制安全:SDS 可以容纳包含 ‘\0’ 字符在内的任意二进制数据,这使得它不仅可以存储文本字符串,还能存储任何形式的二进制数据。
-
兼容 C 字符串:虽然 SDS 是自定义类型,但由于末尾仍然保留了 ‘\0’ 结束符,所以可以直接传递给 C 语言标准库中的字符串处理函数使用。
SDS 常用操作
Redis 提供了一系列针对 SDS 的 API 函数,例如:
sdsnewlen()
或sdsempty()
创建新的 SDS 字符串;sdscat()
、sdscatsds()
、sdscpy()
等函数用于拼接、复制字符串;sdsgrowzero()
动态调整 SDS 缓冲区大小;sdsfree()
释放 SDS 所占用的内存。
这些函数在执行过程中都会考虑到 SDS 的特性,以确保字符串操作高效、安全并且避免常见陷阱。通过这样的设计,Redis 在处理大量字符串操作时获得了显著的性能优势。