Redis SDS的5种类型

Redis SDS的5种类型

1. 结构概览与区别

为了极致地节省内存,Redis 开发者并没有使用一种通用的结构,而是根据字符串的长度,定义了五种不同的 Header 结构。这五种类型的核心区别在于:用于保存字符串长度(len)和分配空间(alloc)的变量类型不同

类型 len变量类型 alloc变量类型 flags占用 最大容纳长度 Header自身占用
sdshdr5 无 (由 flags 位域存储) 1字节 31 字节 1字节
sdshdr8 uint8_t (1字节) uint8_t(1字节) 1字节 255 字节 3字节
sdshdr16 uint16_t (2字节) uint16_t(2字节) 1字节 64 KB 5字节
sdshdr32 uint32_t (4字节) uint32_t(4字节) 1字节 4 GB 9字节
sdshdr64 uint64_t (8字节) uint64_t(8字节) 1字节 $2^{64}-1$ 字节 17字节

⚠️:Header表示元数据信息,即为 flag + len + allocsdshd5没有 lenalloc

2. sdshdr5:特殊的“超短”结构

1
2
3
4
5
6
struct __attribute__ ((__packed__)) sdshdr5 {
unsigned char flags; /* 1字节:高 5 位存储长度,低 3 位存储类型标识 */
char buf[];
};

// flags: xxxxx000 ===> SDS_TYPE_5(xxxxx存储buf长度)

由于没有 alloc 字段,它无法记录剩余可用空间,因此不适合频繁修改的字符串。在 Redis 中,这种类型通常只用于只读的短键名。

3. sdshdr8/16/32/64:标准结构

1
2
3
4
5
6
7
8
9
10
11
struct __attribute__ ((__packed__)) sdshdr8 {
uint8_t len; /* 已使用的长度 */
uint8_t alloc; /* 已分配的总长度(不含头和结束符) */
unsigned char flags; /* 低3位表示类型,高5位未使用 */
char buf[]; /* 字符数组 */
};

// flags: 1 = 00000001 ===> SDS_TYPE_8
// flags: 2 = 00000010 ===> SDS_TYPE_16
// flags: 3 = 00000011 ===> SDS_TYPE_32
// flags: 4 = 00000100 ===> SDS_TYPE_64

__attribute__ ((__packed__)):这是 C 语言的一个特性,告诉编译器取消结构体对齐。这样 Header 就会紧凑排列,不会在变量之间插入填充字节,进一步压榨内存空间。

4. 为什么要分这么多类?

Redis 作为内存数据库,内存利用率是生命线。

  1. 内存节省:如果你只需要存储一个长度为 10 的字符串,用 sdshdr8 只需要 3 字节的 Header;如果强制用 sdshdr64,光 Header 就要 17 字节。在拥有数亿个 Key 的环境下,这种差距会被放大到 GB 级别

  2. 动态升级:当一个字符串因为追加(Append)操作而增长时,Redis 会检测当前 Header 是否还能容纳新长度。如果不够,Redis 会自动将其升级为更高阶的 SDS 类型

    注意⚠️:升级是单向的。 为了减少频繁内存重分配带来的开销,Redis 官方实现中,SDS 结构通常不会在字符串变小时自动“降级”。即使你把一个巨大的字符串截断得很短,它依然会保留之前的 Header 类型和已分配的内存,直到该 Key 被删除或重写。如果未来又要拼接这个字符串,这些多出来的空间就能派上用场。


Redis SDS的5种类型
http://example.com/2026/02/05/Redis-SDS的5种类型/
作者
Kon4tsu
发布于
2026年2月5日
许可协议