壹、sds

(SDS)Simple Dynamic String, 从字面意思上来简单理解就是简单的动态字符,其为具有动态增加空间的能力,扩容不需要使用者担心。
好的咱们来看一下他的数据结构吧
这个版本是黄所注释的3.0版本

typedef char *sds;      //注意,sds其实不是一个结构体类型,而是被typedef的char*,好处见下文struct sdshdr {unsigned int len;   //记录buf中已经使用的长度  等于SDS所保存的字符串的长度unsigned int free;  //buf中未使用的长度char buf[];         //柔性数组buf
};

sds遵循c字符串以空字符结尾的惯例,但是保存空字符的一字节空间不计算在sds的len属性之中,并且为空字符分配额外的一字节空间,使用这一惯例的好处是可直接重用c字符串函数库里面的额函数,这也符合软件工程的思想。

此处应有图(没图你说个毛)

但是在Redis 3.2 版本中,对数据结构做出了修改,针对不同的长度范围定义了不同的结构,如下,这是目前的结构:

typedef char *sds;      struct __attribute__ ((__packed__)) sdshdr5 {     // 对应的字符串长度小于 1<<5unsigned char flags; /* 3 lsb of type, and 5 msb of string length */char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 {     // 对应的字符串长度小于 1<<8uint8_t len; /* used */                       //目前字符串的长度uint8_t alloc;                                //已经分配的总长度unsigned char flags;                          //flag用3bit来标明类型,类型后续解释,其余5bit目前没有使用char buf[];                                   //柔性数组,以'\0'结尾
};
struct __attribute__ ((__packed__)) sdshdr16 {    // 对应的字符串长度小于 1<<16uint16_t len; /* used */uint16_t alloc; /* excluding the header and null terminator */unsigned char flags; /* 3 lsb of type, 5 unused bits */char buf[];
};
struct __attribute__ ((__packed__)) sdshdr32 {    // 对应的字符串长度小于 1<<32uint32_t len; /* used */uint32_t alloc; /* excluding the header and null terminator */unsigned char flags; /* 3 lsb of type, 5 unused bits */char buf[];
};
struct __attribute__ ((__packed__)) sdshdr64 {    // 对应的字符串长度小于 1<<64uint64_t len; /* used */uint64_t alloc; /* excluding the header and null terminator */unsigned char flags; /* 3 lsb of type, 5 unused bits */char buf[];
};

新版带来的好处就是针对长度不同的字符串做了优化,选取不同的数据类型uint8_t或者uint16_t或者uint32_t等来表示长度、一共申请字节的大小等。上面结构体中的 attribute ((packed)) 设置是告诉编译器取消字节对齐,则结构体的大小就是按照结构体成员实际大小相加得到的。(这也是我第一次了解到的) 记得小组每年的纳新面试题,结构体的大小是必考的,试想当年如果能答出来这个的话,会不会刮目呢。


贰、SDS的优劣

typedef char *sds;

e.m.

typedef char *sds;
sds string = sdsnew ("hello world!");  // 生成这样的一个sds。

我们通过sdsnew返回的实际上是一个char * 类型的指针,这个指针指向的是字符串的开始位置,它的头部信息是在字符串前面分配的,这样带来的好处有:

  • sds可以传给任何 char * 为参数的函数,复用c 函数。
  • string[0], 可以直接访问单个字符,对长度进行检测,杜绝缓冲区溢出。
  • 分配的空间地址是连续的(存疑),对高速缓存的命中率更加友好,
  • 二进制安全,读完的标志为len - free, 可存二进制数据。
  • 获取长度的复杂度为O(1),
    除此之外需要重点介绍的就是关于sds的内存分配策略(主要指修改时),
    redis 作为数据库,经常被用于速度要求严苛,数据被频繁修改的场合,如果每次修改字符串的时候都去执行一次内存重新分配,那么光是执行内存重新分配都会占去很大一部分时间,频繁发生的话,会对性能造成影响。

为了避免之中缺陷,sds解除了字符串长度与底层数组的关联: 在SDS中,buf的长度不一定是字符数量加1,数组里边可以包含未使用的字节,由SDS中的free来记录。

通过未使用空间,SDS使用了空间预分配和惰性空间释放的两种优化策略。

1.空间预分配

空间预分配用于优化SDS的字符串增长操作:即不仅分配修改所需空间,还会额外分配未使用空间。
其中未使用的空间的数量由一下公式决定

  • 进行修改之后,sds的长度(len的属性值)将小于1MB,那么程序分配和len属性同样大小的未使用空间,举个例子,修改之后len将变为12,则程序会另外分配13个未使用的字节,总长为 len = 12+12+1 = 25
    (不要忘记额外的一字节用于保存空字符串)。
  • 如果大于1MB,会分配1MB的未使用空间。举个例子,sds变为12MB,那么程序会分配1MB的未使用空间,则len = 12MB+1MB+1byte。

通过这种预分配策略,SDS连续增长N次字符串所需要的内存冲分配次数从N次将为最对N次。

2.惰性空间释放

用有优化SDS缩短操作:当字符串缩短时,并不立即使用内存重新分配回收多出来的字符,而是使用free把这些字节的长度记录起来等待将来使用。
可能你会担心惰性空间释放策略会导致内存浪费。Never mind,SDS提供相应的api 让我们在有需要的时候,真正的释放SDS的未使用空间。

要说缺点也有:虽然用这么多手段提高效率,但是我们并不知道内部是否重新分配了空间。

可以预见到的是,如果程序中别的地方使用了这个字符串,一经改变之后,没有更新地址,那么原地址就会失效,所以这一定要注意,索性这些问题redis已经帮我们处理就好了,我们只需要拿来用就可以啦!!!

Redis源码阅读笔记 ----数据结构部分 sds相关推荐

  1. Redis源码阅读笔记-动态字符串(SDS)结构

    2019独角兽企业重金招聘Python工程师标准>>> Redis中采用自定义的结构来保存字符串,在sds.h中: /* Note: sdshdr5 is never used, w ...

  2. Redis源码阅读笔记(1)——简单动态字符串sds实现原理

    首先,sds即simple dynamic string,redis实现这个的时候使用了一个技巧,并且C99将其收录为标准,即柔性数组成员(flexible array member),参考资料见这里 ...

  3. Redis源码阅读笔记(二)list双向链表结构

    - list简介 相对于sds来说,list并没有太多新的想法和机制,就和自己实现一个双向链表差不多,主要有节点结构.链表结构和迭代器结构三个部分. 只有一个比较新奇的想法是,因为在存储节点的值时,使 ...

  4. 出招吧!腾讯专家手敲《Redis源码日志笔记》,不服来对打!

    引言 本文分为六个部分,包括 Redis 源码日志,服务框架,基础数据结构,内功心法,应用,其他,从源码层面循序渐进的了解Redis.可以快速.有效地了解Redis 的内部构造以及运作机制,更好.更高 ...

  5. redis源码阅读-持久化之RDB

    持久化介绍: redis的持久化有两种方式: rdb :可以在指定的时间间隔内生成数据集的时间点快照(point-in-time snapshot) aof : 记录redis执行的所有写操作命令 根 ...

  6. redis源码阅读-zset

    前段时间给小伙伴分享redis,顺带又把redis撸了一遍了,对其源码,又有了比较深入的了解.(ps: 分享的文章再丰富下再放出来). 数据结构 我们先看下redis 5.0的代码.本次讲解主要是zs ...

  7. 代码分析:NASM源码阅读笔记

    NASM源码阅读笔记 NASM(Netwide Assembler)的使用文档和代码间的注释相当齐全,这给阅读源码 提供了很大的方便.按作者的说法,这是一个模块化的,可重用的x86汇编器, 而且能够被 ...

  8. CI框架源码阅读笔记4 引导文件CodeIgniter.php

    到了这里,终于进入CI框架的核心了.既然是"引导"文件,那么就是对用户的请求.参数等做相应的导向,让用户请求和数据流按照正确的线路各就各位.例如,用户的请求url: http:// ...

  9. 【Flink】Flink 源码阅读笔记(20)- Flink 基于 Mailbox 的线程模型

    1.概述 转载:Flink 源码阅读笔记(20)- Flink 基于 Mailbox 的线程模型 相似文章:[Flink]Flink 基于 MailBox 实现的 StreamTask 线程模型 Fl ...

最新文章

  1. java中缀表达式转后缀表达式(逆波兰算法)
  2. 民族、学历学位、所学专业、、专业技术职务 对应表
  3. c++中list容器
  4. [LeetCode]题解(python):008-String to Integer (atoi)
  5. 大数据Hadoop原理学习(HDFS,MAPREDUCE,YARN)
  6. c语言源程序自动评判系统,C语言源程序的自动评判毕业设计论文
  7. CentOS 7.4 上如何安装 tomcat 9
  8. mysql 6位随机数_MySQL生成固定位数的随机数
  9. 第一节:setTimeout和setInterval定时器
  10. 初学者指南:为开源做贡献
  11. 湖大离散数学实验代码汇总
  12. leetcode第1282题
  13. Duilib介绍-3
  14. halo博客:如何加快搜索引擎收录网站速度
  15. 物联网应用之远程控制
  16. 通过 api 和 keycloak 理解OIDC认证
  17. 删除Linux虚拟机中的/dev/sdb磁盘步骤
  18. python绘制动态Julia集,超炫酷
  19. ipad协议最新版本
  20. 机器视觉实用工具集NO.16——指定监控区域运动物体闯入检测工具

热门文章

  1. ctf解题--当眼花的时候,会显示两张图(隐写)
  2. Keil MDK又来了一个小升级
  3. 将MindManager添加到鼠标右键新建项
  4. bugly android 错误不上报,Flutter Android 端集成 Bugly 的异常上报和升级功能
  5. mysql数据库基础知识点总结--看完即入门
  6. 一周上榜新书:强化学习、深度学习最受程序员关注
  7. 自学编程,痛并快乐着
  8. vue集成svg-sprite-loader
  9. scratch双语教师课件文档手册 1.初识scratch
  10. 微趣能Weiqn(微信源码下载)V1.5.0.2beta官方版