• 理解动态字符串SDS


    参考1
    参考2

    什么是动态字符串(SDS)

    动态字符串(Simple Dynamic String)是Redis五大数据结构之一。
    SDS在Redis中有以下作用

    1. 作为Redis中的字符串对象
    2. 替代C语言中的char*类型

    为什么需要SDS替代char*

    SDS相比于char*有以下优势

    1. 支持O(1)的查询字符串长度strlen(s)操作
    2. 修改字符串时对于内存的重分配较少
    3. 二进制安全
    4. 杜绝缓冲区溢出

    O(1)的strlen()

    SDS中是通过一个len字段存储字符串长度的。
    它不以''决定字符串结尾
    不需要遍历字符串来得到字符串长度

    typedef char* sds
    
    struct sdshdr{
    	unsigned int len;
    	unsigned int free;
    	char* buf;
    }
    

    内存重分配更少

    从上面的结构中可以发现,SDS还有个free字段。
    这个字段是用于内存预分配和惰性释放的,用于存储预分配内存后字符串的额外长度。

    C语言中n个字符的字符串势必要n+1个连续空间存储,每次修改都需要对这个字符数组重新分配内存。

    redis为了优化内存重分配次数,采取了这种策略。

    修改字符串时,会对字符串空间进行预分配

    • 如果修改后的字符串长度大于原来分配的字符串长度,那么会基于修改后字符串长度,给SDS分配额外内存(SDS_MAX_PREALLOC在redis中为1MB)
      • after_modified.len < SDS_MAX_PREALLOC, 额外分配after_modified.len(free中存储额外长度)
      • after_modified.len >= SDS_MAX_PREALLOC,额外分配SDS_MAX_PREALLOC
    • 如果修改后的字符串长度小于于原来分配的字符串长度,直接用预分配好的内存
    sds sdsMakeRoomFor(sds s, const char *t, size_t len) 
    { ... 
    if (newlen < SDS_MAX_PREALLOC) 
    	newlen *= 2; 
    else newlen += SDS_MAX_PREALLOC; 
    
    newsh = realloc(sh, sizeof(struct sdshdr) + newlen + 1); 
    
    if (newsh == NULL) 
    	return  NULL; 
    
    newsh->free = newlen - len; return newsh->buf; 
    }
    

    这种策略在当字符串长度不断缩减时,可能会导致一个问题: 字符串占用了过多不必要的内存
    但Redis也为其设计了相应策略

    惰性空间释放
    上面暗示了,redis不会在字符串长度减少时对多余空间立即释放,那么什么时候会释放不必要的多余空间呢?

    当SDS的字符串占用率低于%25时
    即free>3*len时,
    SDS会释放掉原来size的(即free+len)的%50的大小,即缩一半

    杜绝缓冲区溢出

    SDS 的空间分配策略完全杜绝了发生缓冲区溢出的可能性,具体的实现在 sds.c 中。通过阅读源码,我们可以明白之所以 SDS 能杜绝缓冲区溢出是因为再调用 sdsMakeRoomFor 时,会检查 SDS 的空间是否满足修改所需的要求(即 free >= addlen 条件),如果满足 Redis 将会将 SDS 的空间扩展至执行所需的大小,在执行实际的 concat 操作,这样就避免了溢出发生

    二进制安全

    C 字符串必须符合某种编码,并且除了字符串的末尾之外,字符串不能包含空字符(),否则会被误认为字符串的末尾。这些限制导致不能保存图片、音频等这种二进制数据。

    但是 Redis 就可以存储二进制数据,原因是因为 SDS 是使用 len 属性值而不是空字符来判断字符串是否结束的。

    另外虽然SDS不以空字符()决定字符串结尾,但它在字符串结尾还是会加上空字符,因此他**兼容C语言的字符串操作函数*

  • 相关阅读:
    PPR的断管
    排水地漏的功能与种类
    PPR管及管件的类型、规格与选用
    水龙头的安装、拆卸与阀芯更换
    为不同的用户生成不同的 Kibana 界面
    如何让匿名的用户访问受限的资源
    Beats processors
    Elasticsearch 开发入门
    Elasticsearch Dockerfile 例子
    燃气热水器的结构与安装
  • 原文地址:https://www.cnblogs.com/nothatdinger/p/14127929.html
Copyright © 2020-2023  润新知