1. Redis原理 SDS(简单动态字符串)
SDS
是 Simple Dynamic String
的首字母缩写,翻译为简单的动态字符串,是 Redis
自定义的一种表示字符串的特殊数据结构。相较于传统的C
语言字符串,SDS
具有更多的功能和更高的性能,当前也会占用更多的内存。
Redis 3.2
之前的版本:
struct sdshdr {
int len; // 记录buf数组中已使用字节的数量,即字符串的实际长度
int free; // 存储剩余(空闲)的空间
char buf[]; // 存储实际数据
};
Redis 3.2
之后的版本,SDS
变成了 5
种数据结构,不同类型的存储空间大小不一样:
/* Note: sdshdr5 is never used, we just access the flags byte directly.
* However is here to document the layout of type 5 SDS strings. */
struct __attribute__ ((__packed__)) sdshdr5 {
unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 {
uint8_t len; // 已用空间的长度,占 4 个字节,不包括 ‘\0’;
uint8_t alloc; // 实际分配长度,占 4 个字节,不包括 ‘\0’;
unsigned char flags; // 标记当前 buf[] 类型( sdshdr8/16/32/64)
char buf[]; // 存储实际数据
};
struct __attribute__ ((__packed__)) sdshdr16 {
uint16_t len; /* used */
uint16_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr32 {
uint32_t len; /* used */
uint32_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
struct __attribute__ ((__packed__)) sdshdr64 {
uint64_t len; /* used */
uint64_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
char buf[];
};
相比于C
语言字符串,SDS
有以下优点:
- 二进制安全
- 查询字符串长度效率高
- 缓冲区溢出保护
- 空间预分配
- 惰性空间释放
2. 二进制安全
在 C
语言中,用字符 \0
表示字符串的结束,如果字符串中本身就有 \0
字符,字符串就会被截断,即非二进制安全,例如 hello \0world
会被截断解析为 hello
。SDS
不受 \0
字符影响,能够准确存储任意二进制数据,因为它基于 len
属性而非遇到 \0
来判断字符串的结束位置。
3. 查询字符串长度效率高
在 C
语言中,获取一个字符串长度时,需对整个字符串进行遍历,直至遇到结束符号。在高并发场景下频繁遍历字符串,势必会造成性能瓶颈。SDS
中于 len
属性记录了字符串的长度,可以直接通过 STRLEN
命令获取长度即可。
4. 缓冲区溢出保护
在 C
语言中,字符串本身不记录可用空间大小,在进行字符串拼接等操作时,如果没有检查就可能会导致缓冲区溢出。SDS
中于 free
属性记录了额外未使用的空间大小,每次进行字符串操作前都会检查剩余空间是否足够,不足时会自动扩容,有效避免了缓冲区溢出问题。
5. 空间预分配
在 C
语言中,每次修改字符串长度(增加或减少)时,通常需要重新分配内存,这可能导致频繁的内存操作。SDS
优化了字符串增长操作,当修改字符串并需对 SDS
的空间进行扩展时,不仅会为SDS
分配修改所必要的空间,还会分配额外的未使用空间(free
属性),下次再修改就先检查未使用空间free
是否满足,满足则不用在扩展空间。有效的减少字符串连续增长操作,减少所产生的内存重分配次数。
6.惰性空间释放
惰性空间释放策略则用于优化 SDS
字符串缩短 操作,当缩短 SDS
字符串后,并不会立即执行内存重分配来回收多余的空间,而是用free
属性将这些空间记录下来,如果后续有增长操作,则可直接使用。