阅读背景:

Redis数据类型之字符串

来源:互联网 

Redis数据类型之字符串


redis的字符串

redis的字符串不是C语言原生的字符串,而是自己构建的称为简略动态字符串(simple dynamic string),简称 SDS,和C语言原生的字符串类似,应用’

Redis数据类型之字符串


redis的字符串

redis的字符串不是C语言原生的字符串,而是自己构建的称为简略动态字符串(simple dynamic string),简称 SDS,和C语言原生的字符串类似,应用’\0’作为结尾。

除打印日志以外,我们操作字符串根本是在应用SDS

SDS的在redis的重要功效

 1. 保留数据库的字符串值
 2. 用作缓冲区buffer

SDS在redis的定义
在源码包下面的src目录下的sds.h 和sds.c

    typedef char *sds;
    /**
     * 保留字符串对象的构造
     */
    struct sdshdr {

        // buf 中已占用空间的长度
        int len;

        // buf 中剩余可用空间的长度
        int free;

        // 数据空间
        char buf[];
    };

为何应用SDS而不是C语言原生的字符串?
SDS有一下特色

  1. 获得字符串长度的时光庞杂度是O(1)
    C语言本省其实不携带本身的长度属性,所以要获得长度必须要遍历一次,这个庞杂度是O(n)
    /**
     * 返回实际的长度
     */
    static inline size_t sdslen(const sds s) {
        struct sdshdr *sh = (void*)(s-(sizeof(struct sdshdr)));
        return sh->len;
    }
  1. 避免缓存区溢出
    这个由C语言的一些列不安全的字符串操作函数可以看出,比如
    strcat(char* dest,const char* src)

看看sds.c中的sdscat()

    //函数原型
    sds sdscat(sds s, const char *t);
    //具体实现
    sds sdscat(sds s, const char *t) {
        return sdscatlen(s, t, strlen(t));
    }
    //sdscatlen
    sds sdscatlen(sds s, const void *t, size_t len) {

        struct sdshdr *sh;

        // 原有字符串长度
        size_t curlen = sdslen(s);

        // 扩大 sds 空间
        // T = O(N)
        s = sdsMakeRoomFor(s,len);

        // 内存不足?直接返回
        if (s == NULL) return NULL;

        // 复制 t 中的内容到字符串后部
        // T = O(N)
        sh = (void*) (s-(sizeof(struct sdshdr)));
        memcpy(s+curlen, t, len);

        // 更新属性
        sh->len = curlen+len;
        sh->free = sh->free-len;

        // 添加新结尾符号
        s[curlen+len] = "\0";

        // 返回新 sds
        return s;
    }

可以看到,sds在进行拼接的时候,首先获得原字符串的长度,然落后行扩大。
3. 减少修正字符串时的内存重新分配
C语言的字符串的首先还是依托字符数组,长度为N的字符串对应的是一个长度为N+1的字符数组,所以,在对字符串进行操作时,都会产生对字符数组的内存重新分配。

对内存的重新分配,一般设计庞杂的算法和体系调用,相比较较耗时,偶尔一次的重新分配还可以接收,但是对redis,频繁的操作,确定效力非常低下。

还记得sds的构造体么?保留了一个未应用的空间。sds通过这个来屏蔽了与底层数组的关联 。

那末sds如何应用这个free来实现减少内存重新分配?

办法一、内存预分配。 优化sds的增加操作

简略的说,就是redis API 在修正sds的时候,如果有触及对sds长度进行扩大,那末,同时扩大sds的free空间长度。如果free足够,那末就应用free空间。

那末扩大多少?free与length又有甚么大小关系?
- 如果扩大以后的sds小于1M,那末,扩大以后的free==length,总的内存大小是 free+length+1byte
- 如果扩大以后的sds大于于1M,那末,free的大小是1M,总长度是1M+length+1byte

办法二、惰性空间释放 优化sds的缩短操作

简略的说,就是redis API 在截断sds的时候,程序其实不立即回收多出来的内存,而是应用free属性来保留这些空间。
实现代码

    sds sdstrim(sds s, const char *cset) {
    struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
    char *start, *end, *sp, *ep;
    size_t len;

    // 设置和记载指针
    sp = start = s;
    ep = end = s+sdslen(s)-1;

    // 修剪, T = O(N^2)
    while(sp <= end && strchr(cset, *sp)) sp++;
    while(ep > start && strchr(cset, *ep)) ep--;

    // 盘算 trim 终了以后剩余的字符串长度
    len = (sp > ep) ? 0 : ((ep-sp)+1);

    // 如果有须要,前移字符串内容
    // T = O(N)
    if (sh->buf != sp) memmove(sh->buf, sp, len);

    // 添加终结符
    sh->buf[len] = "\0";

    // 更新属性
    sh->free = sh->free+(sh->len-len);
    sh->len = len;

    // 返回修剪后的 sds
    return s;
}

二进制安全

C语言的字符串只能用来保留ANSI字符,由于不能包括空字符。
所有的sds的api都会以处置二进制的方法来处置保留在sds字节数组的数据,不对这些数据进行任何的处置。

兼容部份c语言的字符串。

版权声明:本篇文章为博主原创文章,未经博主许可不得转述。

’作为结尾。redis的字符串不是C语言原生的字符串,而




你的当前访问异常,请进行认证后继续阅读剩余内容。

分享到: