zoukankan      html  css  js  c++  java
  • 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. 边界检查

  • 相关阅读:
    10 种保护 Spring Boot 应用的绝佳方法
    Redis 如何分析慢查询操作?
    Spring Boot 主类及目录结构介绍
    Redis 再牛逼,也得设置密码!!
    Spring Data Redis 详解及实战一文搞定
    Spring Boot Redis Cluster 实战干货
    超详细的 Redis Cluster 官方集群搭建指南
    Redis Linux 安装运行实战全记录
    hdu 4790 Just Random (思路+分类计算+数学)
    poj 1328 Radar Installation(贪心)
  • 原文地址:https://www.cnblogs.com/jabnih/p/4733260.html
Copyright © 2011-2022 走看看