设计目标与背景
Redis 的 SDS(Simple Dynamic String)是替代 C 原生字符串的核心数据结构,解决了传统 C 字符串的三大痛点:
- 缓冲区溢出风险:C 字符串拼接时需手动管理内存
- 长度计算低效:strlen() 需要 O(n) 遍历
- 二进制不安全:无法存储包含空字符的数据
SDS 结构解析
Redis 3.2 后采用柔性数组成员优化后的结构:
struct __attribute__ ((__packed__)) sdshdr8 {
uint8_t len; // 已用长度
uint8_t alloc; // 总分配空间(不含header)
unsigned char flags;// 类型标识(低3位)
char buf[]; // 柔性数组成员
};
- 类型分级:sdshdr5(淘汰)/8/16/32/64,根据长度自动选择
- 内存对齐:使用 packed 取消结构体对齐,节省内存
- 空间预分配:扩容时按需分配,<1MB 时加倍,free = len,≥1MB 时加 1MB,free= 1M
核心特性
- 二进制安全:通过 len 字段记录长度,允许存储任意二进制数据
- 惰性空间释放:缩短时不立即回收内存,等待后续复用
- 常数级操作:
- O(1) 获取字符串长度
- 自动扩容保障追加操作效率
- 兼容 C 标准:保留 null-terminated 特性,可直接使用部分 C 函数
应用场景
- 字符串存储(SET/GET 命令底层实现)
- AOF 缓冲区和客户端输入缓冲区
- 保存二进制数据(如图片哈希值)
- 高效执行 APPEND 等字符串操作
性能优势对比
| 操作 | C 字符串 | SDS |
|---|---|---|
| strlen() | O(n) | O(1) |
| append 操作 | O(n) | O(n)均摊 |
| 缓冲区安全 | 否 | 是 |
| 内存分配次数 | 每次修改 | 预分配优化 |
学习总结
SDS 作为 Redis 的基础数据结构,通过空间预分配、类型分级、柔性数组成员等设计,在内存效率与操作性能之间达到平衡。其设计思想对理解 Redis 的高性能特性具有重要意义,建议通过阅读 sds.h/sds.c 源码深入学习具体实现细节。