Redis之SDS

一、SDS的定义

Redis自己构建了一种名为简单动态字符串SDS的抽象类型,并将SDS用作Redis的默认字符串表示。

typedef char *sds;

struct sdshdr {
    int len;
    int free;
    char buf[];
};

其中,len代表存储的字符串长度,free代表buf中剩余存储空间,buf用来存储字符串。
倘若使用指针char *buf,分配内存需要两个步骤:一次分配结构体,一次分配char *buf,在释放内存的时候也要释放两次内存:一次为char *buf内存,一次为结构体内存。而使用长度为0的字符数组可以将分配和释放内存的次数都降低为1,从而简化了内存的管理。并且长度为0的数组,char buf[]占用内存空间为0。
为什么是typedef char *sds,而不是typedef struct sdshdr *sds呢?
看下面两个方法:

//返回sds实际保存的字符串长度
static inline size_t sdslen(const sds s) {
    struct sdshdr *sh = (void*)(s - (sizeof(struct sdshdr))); 
    return sh->len;
}

//返回可用空间长度
static inline size_t sdsavail(const sds s) {
    struct sdshdr *sh = (void*)(s - (sizeof(struct sdshdr)));
    return sh->free;
}

用 (void*)(s - (sizeof(struct sdshdr)))来获取struct sdshdr对象,再看初始化方法:

sds sdsnewlen(const void *init, size_t initlen) {
    struct sdshdr *sh;
    if (init) {
        sh = zmalloc(sizeof(struct sdshdr) + initlen + 1);//加1存储'\0'
    }
    else {
        sh = zcalloc(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;
}

返回的是指向buf的地址,而不是struct sdshdr的首地址,所以typedef char *sd。
其中char buf[]占用内存空间为0,可知sizeof(struct sdshdr)为8。
struct sdshdr sh = (void)(s - (sizeof(struct sdshdr)));在C++编译器中会出错,void*不能用于初始化struct sdshdr *对象。

二、空间预分配与惰性空间释放
1、空间预分配
对SDS中buf进行扩展的代码:

sds sdsMakeRoomFor(sds s, size_t addlen) {
    struct sdshdr *sh, *newsh;
    size_t free = sdsavail(s);
    size_t len, newlen;
    if (free >= addlen) return s;
    len = sdslen(s);
    sh = (void*)(s - (sizeof(struct sdshdr)));
    newlen = (len + addlen);
    if (newlen < SDS_MAX_PREALLOC)
        newlen *= 2;
    else
        newlen += SDS_MAX_PREALLOC;
    newsh = zrealloc(sh, sizeof(struct sdshdr) + newlen);
    if (newsh == NULL) return NULL;
    newsh->free = newlen - len;
    return newsh->buf;
}

其中,SDS_MAX_PREALLOC=1024*1024,即若对SDS修改后,其长度小于1M,则程序分配和len属性同样大小的未使用空间;若对SDS修改后,其长度不小于1M,则程序分配1M的未使用空间。
2、惰性空间释放
惰性空间释放用于优化SDS字符串缩短操作,当SDS的API需要缩短SDS保存的字符串时,程序并不立即使用内存重分配来回收缩短后多出来的字节,而是用free属性将这些字节的数量记录起来,等待将来使用。
三、二进制安全
C字符串中除了末尾之外,字符串里不能包含空字符,否则最先被程序读入的空字符将被误认为是字符串结尾,这些限制使得C字符串只能保存文本数据,不能保存图片、音频、视频、压缩文件等二进制数据。
SDS使用len属性的值判断字符串是否结束,而不是空字符串,因此用SDS保存二进制数据是可以的。

sds的优点:
1、在len属性中记录了SDS本身的长度,所以获取一个SDS长度的时间复杂度仅为O(1).
2、校验剩余空间大小,杜绝了缓冲区溢出。
3、空间预分配和惰性空间释放。
4、二进制存储安全
5、兼容部分C字符串函数

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值