Redis 不直接使用 C 语言原生字符串,而是构建了 **SDS(Simple Dynamic String)**、**双向链表**、**字典(哈希表)** 等基础数据结构,支撑上层 String、List、Hash、Set、ZSet 五大对象类型。本文深入剖析前三大底层结构的设计精妙。
struct sdshdr {
int len; // 已用长度
int free; // 剩余可用空间
char buf[]; // 字节数组(柔性数组)
};

| 特性 | C 字符串 | SDS |
|---|---|---|
| 获取长度 | O(N) 遍历 | O(1) 读取 len |
| 缓冲区溢出 | 易发生(strcat 不检查) | 杜绝(修改前自动扩容) |
| 修改内存重分配 | 每次必重分配 | 空间预分配 + 惰性释放,N 次修改最多 N 次分配 |
| 二进制安全 | 否(以 \0 结尾) | 是(靠 len 判断结束,可存图片/视频) |
| 兼容 C 字符串函数 | 全兼容 | 部分兼容(buf 末尾保留 \0) |
free,减少 realloc 调用。realloc,仅更新 free,供下次增长复用。len 而非 \0,完美支持任意二进制数据。

// 节点
typedef struct listNode {
struct listNode *prev;
struct listNode *next;
void *value;
} listNode;
// 容器
typedef struct list {
listNode *head;
listNode *tail;
unsigned long len;
void *(*dup)(void *ptr); // 值复制函数
void (*free)(void *ptr); // 值释放函数
int (*match)(void *ptr, void *key); // 值对比函数
} list;

prev/next 指针,前驱后继访问 O(1)。prev=NULL,尾 next=NULL。head/tail,首尾操作 O(1)。len,获取长度 O(1)。void* value + 三个函数指针,支持任意类型值的深拷贝、释放、比较。// 1. 哈希表节点
typedef struct dictEntry {
void *key;
union { void *val; uint64_t u64; int64_t s64; } v;
struct dictEntry *next; // 链地址法解决冲突
} dictEntry;
// 2. 哈希表
typedef struct dictht {
dictEntry **table; // 数组指针
unsigned long size; // 大小
unsigned long sizemask; // 掩码 (size-1),用于计算索引
unsigned long used; // 已用节点数
} dictht;
// 3. 字典
typedef struct dict {
dictType *type; // 类型特定函数(哈希、键值复制/释放/比较)
void *privdata; // 私有数据
dictht ht[2]; // 两个哈希表:正常用 ht[0],rehash 时用 ht[1]
int rehashidx; // rehash 进度索引,-1 表示未进行
} dict;

数组索引冲突时,新节点插入链表头部(next 指向旧节点)。

负载因子 used/size 超阈值触发扩容,过小触发缩容。
ht[0].used * 2 的 2^n;缩容为第一个 ≥ ht[0].used 的 2^n。rehashidx=0,每次 CRUD 操作顺带迁移 ht[0][rehashidx] 链表至 ht[1],rehashidx++。ht[0] 全部迁移完,释放 ht[0],ht[1] 变 ht[0],新建空 ht[1],rehashidx=-1。

将 O(N) 的集中式迁移分摊到每次操作中,避免单次操作延迟飙升,保证 Redis 高性能特性。
SDS 解决字符串操作的安全与效率;链表提供有序、灵活的序列存储;字典以渐进式 Rehash 的哈希表实现高性能键值映射。这三大基石支撑了 Redis 丰富的上层数据类型与极致性能。