前言

项目里用到了redis数据结构,不想只是简单的调用api,这里对我的读书笔记做一下记录。原文地址: http://www.redisbook.com/en/latest/internal-datastruct/sds.html

数据类型定义

与sds实现有关的数据类型有两个,一个是 sds:

  1. // 字符串类型的别名
  2. typedef char *sds;

另一个是 sdshdr:


  1. // 持有sds的结构
  2. struct sdshdr {
  3. // buf中已经被使用的字符串空间数量
  4. int len;
  5. // buf中预留字符串的空间数量
  6. int free;
  7. // 实际存储字符串的地方
  8. char buf[];
  9. };

其中,sds只是字符串数组类型char*的别名,而sdshdr用于持有和保存sds的信息


比如,sdshdr.len可以用于在O(1)的复杂度下获取sdshdr.buf中存储的字符串的实际长度,而sdshdr.free则用于保存sdshdr.buf中还有多少预留空间

(这里sdshdr应该是sds handler的缩写)

将sdshdr用作sds

sds模块对sdshdr结构使用了一点小技巧:通过指针运算,它使得sdshdr结构可以像sds类型一样被传值和处理,并在需要的时候恢复成sdshdr类型

通过下面的函数定义来理解这个技巧

sdsnewlen 函数返回一个新的sds值,实际上,它创建的却是一个sdshdr结构:

  1. sds sdsnewlen(const void *init, size_t initlen)
  2. {
  3. struct sdshdr *sh;
  4.  
  5. if (init) {
  6. // 创建
  7. sh = malloc(sizeof(struct sdshdr) + initlen + 1);
  8. } else {
  9. // 重分配
  10. sh = calloc(1, sizeof(struct sdshdr) + initlen + 1);
  11. }
  12.  
  13. if (sh == NULL) return NULL;
  14.  
  15. sh->len = initlen;
  16. sh->free = 0; // 刚开始free为0
  17.  
  18. if (initlen && init) {
  19. memcpy(sh->buf, init, initlen);
  20. }
  21. sh->buf[initlen] = '\0';
  22.  
  23. // 只返回sh->buf这个字符串部分
  24. return (char *)sh->buf;
  25. }

通过使用变量持有一个sds的值,在遇到那些只处理sds值本身的函数时,可以直接将sds传给它们。比如说,sdstoupper 函数就是其中的一个例子:


  1. static inline size_t sdslen(const sds s)
  2. {
  3. // 从sds中计算出相应的sdshdr结构
  4. struct sdshdr *sh = (void *)(s - (sizeof(struct sdshdr)));
  5.  
  6. return sh->len;
  7. }
  8.  
  9. void sdstoupper(sds s)
  10. {
  11. int len = sdslen(s), j;
  12.  
  13. for (j = 0; j < len; j ++)
  14. s[j] = toupper(s[j]);
  15. }

这里有一个技巧,通过指针运算,可以从sds值中计算出相应的sdshdr结构:


sds虽然是指向char *的buf(ps:并且空数组不占用内存空间,数组名即为内存地址),但是分配的时候是分配sizeof(struct sdshdr) + initlen + 1的,通过sds - sizeof(struct sdshdr)可以计算出struct sdshdr的首地址,从而可以得到len和free的信息





sdsavail 函数就是使用这中技巧的一个例子:

  1. static inline size_t sdsavail(const sds s)
  2. {
  3. struct sdshdr *sh = (void *)(s - (sizeof(struct sdshdr)));
  4.  
  5. return sh->free;
  6. }

内存分配函数实现

和Reids 的实现决策相关的函数是 sdsMakeRoomFor :

  1. sds sdsMakeRoomFor(sds s, size_t addlen)
  2. {
  3. struct sdshdr *sh, *newsh;
  4. size_t free = sdsavail(s);
  5. size_t len, newlen;
  6.  
  7. // 预留空间可以满足本地拼接
  8. if (free >= addlen) return s;
  9.  
  10. len = sdslen(s);
  11. sh = (void *)(s - (sizeof(struct sdshdr)));
  12.  
  13. // 设置新sds的字符串长度
  14. // 这个长度比完成本次拼接实际所需的长度要大
  15. // 通过预留空间优化下次拼接操作
  16. newlen = (len + addlen);
  17. if (newlen < 1024 * 1024)
  18. newlen *= 2;
  19. else
  20. newlen += 1024;
  21.  
  22. // 重新分配sdshdr
  23. newsh = realloc(sh, sizeof(struct sdshdr) + newlen + 1);
  24. if (newsh == NULL) return NULL;
  25.  
  26. newsh->free = newlen - len;
  27.  
  28. // 只返回字符串部分
  29. return newsh->buf;
  30. }

这种内存分配策略表明,在对sds 值进行扩展(expand)时,总会预留额外的空间,通过花费更多的内存,减少了对内存进行重分配(reallocate)的次数,并优化下次扩展操作的处理速度


再把redis的如果实现对sds字符串扩展的方法贴一下,很不错的思路:

  1. /**
  2. * 按长度len扩展sds,并将t拼接到sds的末尾
  3. */
  4. sds sdscatlen(sds s, const void *t, size_t len)
  5. {
  6. struct sdshdr *sh;
  7.  
  8. size_t curlen = sdslen(s);
  9.  
  10. // O(N)
  11. s = sdsMakeRoomFor(s, len);
  12. if (s == NULL) return NULL;
  13.  
  14. // 复制
  15. memcpy(s + curlen, t, len);
  16.  
  17. // 更新len和free属性
  18. sh = (void *)(s - (sizeof(struct sdshdr)));
  19. sh->len = curlen + len;
  20. sh->free = sh->free - len;
  21.  
  22. // 终结符
  23. s[curlen + len] = '\0';
  24.  
  25. return s;
  26. }
  27.  
  28. /**
  29. * 将一个char数组拼接到sds 末尾
  30. */
  31. sds sdscat(sds s, const char *t)
  32. {
  33. return sdscatlen(s, t, strlen(t));
  34. }

OK,这里暂时对sds(简单动态字符串)的学习告一段落,继续写业务逻辑代码,很好奇hashs和sets结构是如何实现!!

Redis设计与实现读书笔记——简单动态字符串的更多相关文章

  1. 【笔记】《Redis设计与实现》chapter2 简单动态字符串

    ------------恢复内容开始------------ 2.1 SDS的定义 struct sdshdr{ // 记录buf数组中已使用字节的数量 // 等于SDS所保存字符串的长度(不含'\0 ...

  2. 小白的Redis学习(一)-SDS简单动态字符串

    本文为读<Redis设计与实现>的记录.该书以Redis2.9讲解Redis相关内容.请注意版本差异. Redis使用C语言实现,他对C语言中的char类型数据进行封装,构建了一种简单动态 ...

  3. Redis源码解析:01简单动态字符串SDS

    Redis没有直接使用C字符串(以'\0'结尾的字符数组),而是构建了一种名为简单动态字符串( simple  dynamic  string, SDS)的抽象类型,并将SDS用作Redis的默认字符 ...

  4. Redis 设计与实现读书笔记一 Redis字符串

    1 Redis 是C语言实现的 2 C字符串是 /0 结束的字符数组 3 Redis具体的动态字符串实现 /* * 保存字符串对象的结构 */ struct sdshdr { // buf 中已占用空 ...

  5. Redis设计与实现读书笔记(二) 链表

    链表作为最基础的数据结构,在许多高级语言上已经有了很好的实现.由于redis采用C语言编写,需要自己实现链表,于是redis在adlist.h定义了链表类型.作者对于这部分没什么好说,源码比较简单,如 ...

  6. Redis设计与实现读书笔记(一) SDS

    作为redis最基础的底层数据结构之一,SDS提供了许多C风格字符串所不具备的功能,为之后redis内存管理提供了许多方便.它们分别是: 二进制安全 减少字符串长度获取时间复杂度 杜绝字符串溢出 减少 ...

  7. <<redis设计和实现>>读书笔记

    redis如何实现主从同步的高效率?? 主从复制的同步有一个命令数据的同步文本,然后利用两个不同服务器的偏移量来进行进行同步,避免每次都是全部同步(并非会保存所有的命令数据,而是会有一个缓冲区(比如1 ...

  8. Redis设计与实现读书笔记——双链表

    前言 首先,贴一下参考链接: http://www.redisbook.com/en/latest/internal-datastruct/adlist.html, 另外真赞文章的作者,一个90后的小 ...

  9. Redis 设计与实现读书笔记一 Redis List

    list结构体 adlist.h/list(源码位置) /* * 双端链表结构 */ typedef struct list { // 表头节点 listNode *head; // 表尾节点 lis ...

随机推荐

  1. thinkphp _complex 复合查询 where多个子组实现

    SELECT * FROM `user` WHERE ( `mobile` = '13824653465' OR `nickname` = 'evan' OR `openid` = '14545-fd ...

  2. poj3624 Charm Bracelet(DP,01背包)

    题目链接 http://poj.org/problem?id=3624 题意 有n个手镯,每个手镯有两个属性:重量W,需求因子D.还有一个背包,它能装下总重量不超过M的手镯.现在将一些镯子装入背包,求 ...

  3. hdoj1233 还是畅通工程(Prime || Kruskal)

    题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=1233 思路 最小生成树问题,使用Prime算法或者Kruskal算法解决. 代码 Prime算法: # ...

  4. Python 学习之list和Tuple类型

    1.创建list L = ['Adam', 95.5, 'Lisa', 85, 'Bart', 59] print(L) print(L[1],L[3],L[5])#索引 不能越界 正向访问 #95. ...

  5. servlet文件下载实例剖析

    package mypack; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; ...

  6. idea导入或打开项目配置问题

    learn项目遇到问题: 1.IntelliJ Idea编译报错:请使用 -source 7 或更高版本以启用 diamond 运算符 file - project structure或者直接快捷键: ...

  7. 使用命令行管理virtualBox

    最近在鼓捣hadoop,装了几台虚拟机,,总感觉gui启动很别扭,后来发现virtualBox有个headless模式,只想说舒服! 常用命令 VBoxManage startvm name|id - ...

  8. java UTF8 HEX

    private final static char[] hexArray = "0123456789ABCDEF".toCharArray(); public static Str ...

  9. apache 监控

    当前加载模块 [root@controller01 ~]# httpd -lCompiled in modules: core.c mod_so.c http_core.c 当前版本[root@con ...

  10. BZOJ 4520 [Cqoi2016]K远点对(KD树)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=4520 [题目大意] 求K远点对距离 [题解] 修改估价函数为欧式上界估价,对每个点进行 ...