Redis简单动态字符串SDS

Redis简单动态字符串SDS

       源码版本:REDIS  5.0.4

       redis没有直接使用C语言当中以’\0’的结尾的字符串,而是实现了自己字符串结构简单动态字符串(simple  dynamic  string, SDS),同时SDS又保留了字符串当中的‘\0’兼容C语言字符串

SDS结构体类似如下:

struct __attribute__ ((__packed__)) sdshdrX
{
    length_type len;
    length_type alloc;
    unsigned char flags;
    char buf[];
};

 

结构体分析:

len代表已使用的缓存,不包括字符串中的‘\0’;

alloc代表已分配的总长度;

sdshdrX当中的X可以为8、16、32、64(其实还有5,redis当中sdshdr5没有做使用,只是起标记作用),与之对应的length_type为uint8_t、uint16_t、uint32_t、uint64_t,length_type的不同意味着起所能存储的字符串最大长度的不同;

flags标识当前SDS字符串length_type的数据类型;

__attribute__ ((__packed__))的作用是告诉编译器取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐,结构体在内存当中紧密排列。

 

SDS内存结构如下:

 

SDS字符串创建函数

sds sdsnewlen(const void *init, size_t initlen);

sds sdsnew(const char *init);

sds sdsempty(void);

sdsnewlen、sdsnew会根据传入init参数创建SDS字符串,sdsempty会创建一个空的SDS字符串,三个函数最终的具体实现均由sdsnewlen来具体实现,函数返回值数据类型为char *指针(typedef char *sds),起指向的位置为buf的起始地址。

sdsnewlen函数内部流程:

  1. 根据传入的initlen获取flags的类型;
  2. 分配内存分配的内存长度为sizeof(struct sdshdrX)+initlen+1,其中sizeof(struct sdshdrX)个字节中存储len、alloc、flags信息,initlen个字节存储传入的字符串,多分配的那一个字节存储‘\0’;
  3. 赋值,返回。

假设执行sds test = sdsnewlen(“Hello world!”, 12);前产生的SDS字符串结构如下:

SDS字符串拼接函数

sds sdscatlen(sds s, const void *t, size_t len);

sds sdscat(sds s, const char *t);

sds sdscatsds(sds s, const sds t);

 

函数的最终实现均由sdscatlen完成,其函数流程如为:

IF 不需要重新分配内存

ELSE

    IF 两个字符串长度之和小于SDS_MAX_PREALLOC

    新长度=字符串长度之和

    ELSE

        新长度=字符串长度之和+SDS_MAX_PREALLOC

    IF length_type没有发生变更

        realloc新长度内存

    ELSE length_type发生变更

        Malloc新长度内存

        更新SDS类型

 拼接字符串

SDS字符串拷贝函数

sds sdscpylen(sds s, const char *t, size_t len);

sds sdscpy(sds s, const char *t);

内部流程与SDS字符串拼接函数类似

 

此外还有如下的中功能的函数

sds sdscatfmt(sds s, char const *fmt, ...);//格式化字符串
sds sdstrim(sds s, const char *cset); //去掉两端重复字符
void sdstolower(sds s);//字母转小写
void sdstoupper(sds s);//字母转大写
int sdscmp(const sds s1, const sds s2); //比较函数

SDS与C字符串对比

       SDS计算长度、拼接字符等时间复杂度为O(1),C字符串的复杂度为O(n),SDS有空间预分配,可以减少内存分配次数,但也由此可能造成内存浪费的现象,但因为SDS的内存分布策略,内存浪费较低。

       因为SDS字符串最后一个字节也复制值为’\0’,并且sds指针指向的起始位置为字符串起始位置所以,SDS字符串也可以使用C的字符串函数,例如printf。

       C字符串不记录自身长度, 修改字符串时不会判断本身是否拥有足够的内存空间, 当内存空间不足时, 则会造成缓冲区的溢出,SDS对字符串进行修改时,先检查内存空间是否满足修改的需要, 若不满足, 则自动扩展SDS的内存空间. 所以使用SDS既不需要手动修改内存空间的大小, 也不会出现缓冲区溢出的情况

你可能感兴趣的:(REDIS源码分析,C,REDIS,NoSql,内存数据库)