Redis String 数据结构演进史

从 C 字符串到 SDS 的性能优化之路

Redis 2.0

SDS 初代版本

2010 年,Redis 首次引入 SDS 作为字符串实现。 此前直接使用 C 字符串(char*),存在缓冲区溢出和 O(n) 长度获取问题。

// sdshdr (Redis 2.0 - 2.6)
int len 4 bytes
int free 4 bytes
char buf[] 柔性数组
头部大小: 8 bytes
O(1) 长度获取 二进制安全 空间预分配 惰性释放
Redis 2.2

APPEND 优化

引入高效的字符串追加(APPEND)操作。通过记录已用长度 len, 避免每次追加都重新分配内存。

// 追加操作流程
1. 检查 free >= 追加长度?
2. 是 → 直接追加,更新 len
3. 否 → 重新分配,预分配策略
APPEND 时间复杂度降低 减少内存重分配
Redis 2.4

预分配策略优化

优化内存预分配算法。当字符串小于 1MB 时,倍增预分配; 大于 1MB 时,每次多分配 1MB。显著减少内存碎片。

// 预分配算法 (Redis 2.4+)
if (newLen < 1MB) {
    alloc = newLen * 2; // 倍增
} else {
    alloc = newLen + 1MB; // 固定增量
}
内存利用率提升 减少碎片
Redis 3.2 ⭐ 重大重构

SDS 类型分桶 + EMBSTR

3.2 是转折点!引入 5 种 SDS 类型,头部从 8 字节压缩到 1-3 字节。 短字符串使用 embstr 编码,string 和 redisObject 连续内存分配。

// SDS_TYPE_5 (短字符串)
flags 1 byte
buf[] 内嵌
头部: 1 byte
// SDS_TYPE_8 (长字符串)
uint8_t flags 1 byte
uint64_t len 8 bytes
uint64_t alloc 8 bytes
char buf[] 柔性数组
头部: 17 bytes
SDS_TYPE_5/8/16/32/64 embstr 编码 44字节阈值 连续内存分配
Redis 4.0

模块化 + 动态类型

引入 Redis Modules API,允许自定义数据类型。 string 类型保持稳定,进一步优化内存分配策略。

• Modules 可扩展 Redis 数据类型
• string 内部优化继续积累
• OBJECT ENCODING 可查看内部类型
Module API 动态类型系统
Redis 7.0+

函数 + 性能巅峰

Redis 7.0 引入 Redis Functions(取代 Lua 脚本), string 类型支持更高效的 int 编码优化(当值为纯数字时)。

// String 内部编码
|--- embstr (≤44 bytes) ---|
|--- raw (>44 bytes) -------|
|--- int (纯数字) ----------|
int 编码优化 Functions Sharded Pub/Sub

SDS vs C 字符串

长度获取

C 字符串 O(n)
SDS O(1)

内存重分配

C 字符串 每次 n 次
SDS 最多 1 次

缓冲区安全

C 字符串 ❌ 易溢出
SDS ✅ 安全

二进制安全

C 字符串 ❌ 遇 \0 截断
SDS ✅ 完整存储

EMBSTR 编码的秘密

embstr = embedded string,将 redisObject 和 sds 分配在同一块连续内存

raw 编码 (>44 bytes)

redisObject (56 bytes)
ptr → sdshdr (独立分配)

两次内存分配 ❌

embstr 编码 (≤44 bytes)

redisObject
SDS_TYPE_5
char buf[] (连续)

一次内存分配 ✅

// 内存布局 (64-bit 系统)
redisObject: type(1) + encoding(1) + lru(4) + refcount(4) + *ptr(8) = 18 bytes
sdshdr5: flags(1) + buf[] = 46 bytes
───────────────────────────────
总计: 64 bytes(CPU cache line 对齐!)

演进总结

Redis String 从最初的简单封装,到今天的高性能动态字符串, 经历了多次精妙的优化:

空间换时间(预分配)→ 类型分桶(SDS_TYPE_X)→ 内存对齐(embstr)

每一个版本都在追求更低的内存开销和更高的访问效率。 这也是 Redis 能够成为内存数据库标杆的核心秘密之一。