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)