sds在Redis中是实现字符串对象的工具,并且完全取代char*.
char*的功能比较单一,不能实现Redis对字符串高效处理的需求,char*的性能瓶颈主要在:计算字符串长度需要使用strlen函数,该函数的时间复杂度是O(N),而在Redis中计算字符串长度的操作十分频繁,O(N)的时间复杂度完全不能接受,sds实现能在O(1)时间内得到字符串的长度值;同时,在处理字符串追加append操作时,如果使用char*则需要多次重新分配内存操作。
通过增加len字段,就可以实现在O(1)时间复杂度内得到字符串的长度,增加free字段,在需要append字符串时,如果free的值大于等于需要append的字符串长度,那么直接追加即可,不需要重新分配内存。sizeof(sdshdr) = 8.
redis巧妙的使用柔性数组技巧,减少结构体占用内存,并可以快速返回字符串长度,弹性增加大小,压缩占用内存空。
建议参考sds.c源文件实现
实例代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef char* sds;
struct sdshdr{
unsigned int len; 字符串长度
unsigned int free;
char buf[]; 字符串
};
sds sdsnewlen(const void *init, size_t initlen) {
struct sdshdr *sh;
if (init) {
sh = malloc(sizeof(struct sdshdr)+initlen+1);
} else {
sh = malloc(sizeof(struct sdshdr)+initlen+1);
}
if (sh == NULL) return NULL;
sh->len = initlen;
sh->free = 0;
if (initlen && init)
memcpy(sh->buf, init, initlen);
sh->buf[initlen] = '\0';
return (char*)sh->buf; 注意:这里返回的是字符串,很好的实现了隐藏结构体
}
sds sdsnew(const char *init) {
size_t initlen = (init == NULL) ? 0 : strlen(init);
return sdsnewlen(init, initlen);
}
static inline size_t sdslen(const sds s) {
struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
return sh->len; 这里直接获取到字符串长度,不用在计算长度
}
int main(int argc, char *argv[])
{
const char *str = "haha hehe lala";
sds a = sdsnew(str);
int n = sdslen(a);
printf("%d\n", n);
printf("%d\n", strlen(str));
return 0;
}
redis利用指针巧妙的隐藏结构体,对外看来跟char*无异
简单动态字符串sds中函数API
函数名称 | 作用 | 复杂度 |
sdsnewlen | 创建一个指定长度的sds,接受一个指定的C字符串作为初始化值 | O(N) |
sdsempty | 创建一个只包含空字符串””的sds | O(N) |
sdsnew | 根据给定的C字符串,创建一个相应的sds | O(N) |
sdsdup | 复制给定的sds | O(N) |
sdsfree | 释放给定的sds | O(1) |
sdsupdatelen | 更新给定sds所对应的sdshdr的free与len值 | O(1) |
sdsclear | 清除给定sds的buf,将buf初始化为””,同时修改对应sdshdr的free与len值 | O(1) |
sdsMakeRoomFor | 对给定sds对应sdshdr的buf进行扩展 | O(N) |
sdsRemoveFreeSpace | 在不改动sds的前提下,将buf的多余空间释放 | O(N) |
sdsAllocSize | 计算给定的sds所占的内存大小 | O(1) |
sdsIncrLen | 对给定sds的buf的右端进行扩展或缩小 | O(1) |
sdsgrowzero | 将给定的sds扩展到指定的长度,空余的部分用\0进行填充 | O(N) |
sdscatlen | 将一个C字符串追加到给定的sds对应sdshdr的buf | O(N) |
sdscpylen | 将一个C字符串复制到sds中,需要依据sds的总长度来判断是否需要扩展 | O(N) |
sdscatprintf | 通过格式化输出形式,来追加到给定的sds | O(N) |
sdstrim | 对给定sds,删除前端/后端在给定的C字符串中的字符 | O(N) |
sdsrange | 截取给定sds,[start,end]字符串 | O(N) |
sdscmp | 比较两个sds的大小 | O(N) |
sdssplitlen | 对给定的字符串s按照给定的sep分隔字符串来进行切割 | O(N) |