简单动态字符串
- Simple Dynamic String是Redis内部自己定义的一种数据类型
- 在Redis内部, 任何包含字符串的键值对都是由SDS实现的
- SDS还被用于缓冲区, 比如AOF缓冲区.
比如以下几个命令
// 设置text为key, "hello world"为value, 两者都为字符串所以都是由SDS实现
set text "hello world"
// 建立一个列表从右端插入, names为key, value即为"john" "lucy"
rpush names "john" "lucy"
上面两个例子key和value都是由string构成那么他们底层就都是由SDS实现的
那么SDS底层在C语言是如何实现的呢
下面给出Redis3.2版本之前的实现
struct sdshdr{
unsinged int len; // 已经使用的字节数量
unsinged int less; // 未使用的字节数量
char buf[]; // 保存字符串的数组
};
给出一个这样的模型代表我用了5个字节, 还有5个字节空闲 , '\0’不算在内
这样使用的好处有
- 1.降低了获取字符串长度的复杂度, 复杂度直接看len就行为O(1)
- 2.通过空间预分配, 减少了修改带来的内存分配
- 3.SDS以’\0’结尾,依然遵守C语言规则, 可以方便的使用C库函数
但还是有一些不好之处 :
len,和free字段占用了4个字节, 对于较短的字符串, 浪费了存储空间
可能有些字符串只占1个字节,甚至更小, 但这两个属性就占了8个字节,浪费比较大.
Redis是把数据存放在内存中, 内存空间小于硬盘空间, 所以内存空间是非常宝贵的并且SDS在Redis中是比较常见的, 一般如果存上千个,上万个数据, 那么浪费的空间就比较大了
Redis3.2之后就将这个问题改进了
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; /* used */
uint8_t alloc; /* excluding the header and null terminator */
unsigned char flags; /* 3 lsb of type, 5 unused bits */
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[];
};
分为5种情况, 小于1字节使用第一个, 1字节使用第二个,2字节使用第三个依次类推