一、SDS(Simple Dynamic String,简单动态字符串)
Redis 底层字符串实现,Redis 的 key、很多 value 底层都是 SDS,没有直接使用 C 原生字符串。
1. C 原生字符串缺陷
- 获取长度:必须遍历,O (n)
- 非二进制安全:遇到
\0就截断,无法保存图片、音频等二进制数据 - 修改字符串容易缓冲区溢出,扩容麻烦
2. SDS 结构体(sdshdr8 示例)
struct __attribute__ ((__packed__)) sdshdr8 {
uint8_t len; // 当前有效字符串长度,不含末尾\0
uint8_t alloc; // 已分配内存总大小(预分配空间),不含末尾\0
unsigned char flags; // 标记SDS类型(5种:SDS_TYPE_5/8/16/32/64,决定header大小)
char buf[]; // 柔性数组,存放字符,末尾保留\0,兼容C函数
};
__packed__:取消内存对齐,节省内存。
3. SDS 动态扩容(内存预分配)
追加字符串时,先判断剩余空间够不够,不够就扩容:
- 新长度 <1M :预分配空间 = 新长度 ×2 (+1 留给
\0) - 新长度 ≥1M :预分配空间 = 新长度 +1M(+1 留给
\0)
目的:减少多次内存 realloc 分配开销
4. SDS 四大优点
- 获取长度 O (1):直接读取
len字段,不用遍历 - 二进制安全 :依靠
len判断字符串结束,不是\0,可存二进制 - 动态扩容 + 内存预分配,减少内存分配次数
- 兼容 C 语言字符串函数:buf 末尾自带
\0
二、IntSet(整数集合,Set 集合底层编码之一)
Redis Set 底层的整数专用压缩存储 ,只在集合全部是整数、元素数量不多时使用;一旦不符合条件,自动转哈希表。
1. 结构体
typedef struct intset {
uint32_t encoding; // 编码类型:INT16 / INT32 / INT64
uint32_t length; // 集合元素总个数
int8_t contents[]; // 有序升序数组,存放整数
} intset;
- 三种编码:
- INTSET_ENC_INT16:每个元素占 2 字节
- INTSET_ENC_INT32:每个元素占 4 字节
- INTSET_ENC_INT64:每个元素占 8 字节
- 特点:
contents数组升序排列,查找用二分查找。
2. IntSet 升级机制(单向升级,不能降级)
场景:原有集合是 INT16,新增数字超出 int16 范围 升级 4 步:
- 升级编码到更大类型(例:INT16 → INT32),扩容数组
- 倒序拷贝原有元素(防止覆盖未拷贝数据)
- 将新元素放到数组末尾
- 修改
encoding和length字段
重点考点:只能从小类型升级到大类型,升级后无法回退降级。
3. IntSet 适用条件
- 集合内全部都是整数
- 元素数量较少;新增非整数元素,会直接转为 hashtable