三、Redis数据结构-动态字符串SDS(simple dynamic string)

1、Redis前言

C语言的传统的字符串(以’\0’结尾的字符数组)表示。

SDS提升字符串的操作效率,同时也可以保存二进制数据。因为传统C字符串符合ASCII编码,这种编码的操作的特点就是:遇零则止 。即,当读一个字符串时,只要遇到’\0’结尾,就认为到达末尾,就忽略’\0’结尾以后的所有字符。因此,如果传统字符串保存图片,视频等二进制文件,操作文件时就被截断了。举例几个常见命令,直观感受下字符串的操作:

127.0.0.1:6379> set str1 Redis
OK
127.0.0.1:6379> get str1
"Redis"
127.0.0.1:6379> type str1
string
127.0.0.1:6379> strlen str1
(integer) 5

详细的相关数据类型的命令操作可以参考查询如下网址:

Redis命令中心(Redis commands) -- Redis中国用户组(CRUG)

 

2、SDS源码,主要熟悉下sds.c和sds.h文件即可

typedef char *sds; // sds兼容传统C风格字符串
struct __attribute__ ((__packed__)) sdshdr8 {  // attribute((packed))作用是取消编译阶段的内存优化对齐功能
uint8_t len; /* used */ // 字符串的长度
uint8_t alloc;
/* excluding the header and null terminator */ // buf指针分配空间的大小
unsigned
char flags; /* 3 lsb of type, 5 unused bits */ // 该字符串的类型 s[-1]就是sdshdr中flags变量
char buf[]; // sds是指向sdshdr结构buf成员的

SDS内存空间扩容机制:

sds sdsMakeRoomFor(sds s, size_t addlen) {
    void *sh, *newsh;
    size_t avail = sdsavail(s); // 剩余容量
    size_t len, newlen, reqlen;
    char type, oldtype = s[-1] & SDS_TYPE_MASK;
    int hdrlen;

    /* Return ASAP if there is enough space left. */
    if (avail >= addlen) return s; // 长度足够无需扩容

    len = sdslen(s); // 字符串长度
    sh = (char*)s-sdsHdrSize(oldtype);
    reqlen = newlen = (len+addlen); // 扩容后的新字符串长度
    assert(newlen > len);   /* Catch size_t overflow */
    if (newlen < SDS_MAX_PREALLOC) // #define SDS_MAX_PREALLOC (1024*1024) 分配内存最大长度为 1MB
        newlen *= 2; // 扩容翻倍
    else
        newlen += SDS_MAX_PREALLOC; // 加一个最大长度

    type = sdsReqType(newlen);
   .....return s;
}

SDS内存空间释放机制:

void sdsclear(sds s) {
    sdssetlen(s, 0);
    s[0] = '\0';
}


static inline void sdssetlen(sds s, size_t newlen) { // inline关键字仅仅是建议编译器做内联展开处理,而不是强制
    unsigned char flags = s[-1];
    switch(flags&SDS_TYPE_MASK) {
        case SDS_TYPE_5:
            {
                unsigned char *fp = ((unsigned char*)s)-1;
                *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);
            }
            break;
        case SDS_TYPE_8:
            SDS_HDR(8,s)->len = newlen; // 字符串空间变为0 
            break;
        case SDS_TYPE_16:
            SDS_HDR(16,s)->len = newlen;
            break;
        ....
    }
}

 

总结一下:本文主要了解下动态字符串的结构和内存空间的扩容和释放机制。针对具体方法函数的使用,有问题时再详细看下源码这里就不做过多的赘述了。

 

posted @ 2022-05-11 00:03  chch213  阅读(87)  评论(0编辑  收藏  举报