• Redis学习——SDS字符串源码分析


    0. 前言

      这里对Redis底层字符串的实现分析,但是看完其实现还没有完整的一个概念,即不太清楚作者为什么要这样子设计,只能窥知一点,需要看完redis如何使用再回头来体会,有不足之处还望告知。

      涉及文件:sds.h/sds.c

    1.  数据结构:  

    1 typedef char *sds;
    2 
    3 struct sdshdr {
    4     unsigned int len;    //buf中已使用的字节数
    5     unsigned int free;    //buf中未使用的字节数
    6     char buf[];        //缓冲区
    7 };

      这里向外提供的api所返回的类型都是sds类型(字符串),这样的话也能够复用一部分的C字符串函数。

      这里采用sdshdr结构,存放了字符串长度信息,保证了二进制数据安全,即不仅可以存放字符串,也可用于存放其它二进制数据

    2. API实现:

      只提取几个API,该文件完整的注释在GitHud上(用户名:jabnih)

    a. sdsnewlen

      创建一个sds字符串,其它几个创建API都是基于这个API。

      创建时采用一次性分配其所需要的空间,即对于buf不进行再次分配,减少了malloc等的调用,同时在释放的时候也减少free次数

     1 //创建一个sds字符串,初始内容为init所指向的内容,buf空间为initlen大小
     2 sds sdsnewlen(const void *init, size_t initlen) {
     3     struct sdshdr *sh;
     4 
     5     //这里需要注意
     6     if (init) {
     7         //init不为空,则使用malloc,所申请的空间不会初始化
     8         sh = zmalloc(sizeof(struct sdshdr)+initlen+1);
     9     } else {
    10         //init为空,使用calloc,所申请的空间会被初始化为0
    11         sh = zcalloc(sizeof(struct sdshdr)+initlen+1);
    12     }
    13 
    14     if (sh == NULL) return NULL;
    15 
    16     sh->len = initlen;
    17     sh->free = 0;
    18     //这里如果init为NULL,则该buf的内容均为0
    19     if (initlen && init)
    20         memcpy(sh->buf, init, initlen);
    21 
    22     sh->buf[initlen] = '';
    23 
    24     return (char*)sh->buf;
    25 }

    b. sdsMakeRoomFor

      该API的内存分配策略为:在小于SDS_MAX_PREALLOC(即1M)时,会预分配出多一倍的空间,在大于该阈值时,每次只预分配多SDS_MAX_PREALLOC内存。

     1  //保证sds字符串有足够的剩余未使用空间(大于或等于addlen)
     2 sds sdsMakeRoomFor(sds s, size_t addlen) {
     3     struct sdshdr *sh, *newsh;
     4     size_t free = sdsavail(s);
     5     size_t len, newlen;
     6 
     7     //其剩余的空间满足addlen大小
     8     if (free >= addlen) return s;
     9 
    10     //不满足addlen大小,需要重新分配
    11     len = sdslen(s);
    12     sh = (void*) (s-(sizeof(struct sdshdr)));
    13     //新空间所需使用的大小为当前sds使用的长度加上addlen
    14     newlen = (len+addlen);
    15     //如果新空间大小比设定的阈值小,则以2倍的增长速度预分配一些空间
    16     if (newlen < SDS_MAX_PREALLOC)
    17         newlen *= 2;
    18     else
    19         //比设定阈值大,则只增加PREALLOC预分配大小
    20         newlen += SDS_MAX_PREALLOC;
    21     //重新分配空间
    22     newsh = zrealloc(sh, sizeof(struct sdshdr)+newlen+1);
    23     if (newsh == NULL) return NULL;
    24 
    25     newsh->free = newlen - len;
    26     return newsh->buf;
    27 }

    c. sdsRemoveFreeSpace

     1  //去除sds字符串中未使用的空间,一般在内存紧张的时候使用
     2 sds sdsRemoveFreeSpace(sds s) {
     3     struct sdshdr *sh;
     4 
     5     sh = (void*) (s-(sizeof(struct sdshdr)));
     6     sh = zrealloc(sh, sizeof(struct sdshdr)+sh->len+1);
     7     sh->free = 0;
     8 
     9     return sh->buf;
    10 }

    d. sdsclear

    1  //清空sds字符串,但是不释放空间
    2 void sdsclear(sds s) {
    3     struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
    4 
    5     sh->free += sh->len;
    6     sh->len = 0;
    7     sh->buf[0] = '';
    8 }

    3. 总结:

      1. 二进制数据安全

      2. 预分配空间,可以懒惰释放,在内存紧张的时候也可以缩减不需要的内存

      3. 使用该API可以实现内存动态扩展(即不需要考虑内存空间是否足够)

      4. 边界检查

  • 相关阅读:
    vue项目 安装
    处理XML的几种方式
    从URL中获取搜索关键字
    Azure VM 远程无法登陆问题(No Remote Desktop License)
    也谈[关于大型网站技术演进的思考--存储的瓶颈]
    UML类图
    Apache Spark探秘:三种分布式部署方式比较
    Standalone Debugging Tools for Windows (WinDbg)
    UML: CIM & PIM
    C# 延迟初始化
  • 原文地址:https://www.cnblogs.com/jabnih/p/4733260.html
Copyright © 2020-2023  润新知