壹、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键盘输入运算符_Java基础学习-三元运算符和键盘录入的基本步骤和使用
  2. SpringMVC接收checkbox传值
  3. leetcode1011
  4. 企业级UML/MDA工具Trufun 2007系列发布!
  5. c#在WinForm中重写ProgressBar控件(带%的显示)
  6. etcd部署集群的三种方式
  7. 流程图制作在云上 https://www.processon.com/
  8. Linux常用命令—文件处理命令—文件处理命令
  9. 【强烈推荐】如何解决JQuery类Post方式的跨域问题 - 空山雪林通用模块设计工作室 - ITeye技术网站...
  10. ArcGIS API for JavaScript——绘制工具(Draw)
  11. python psutil 进行系统管理 no.2
  12. 百度AI之图像识别SDK:车牌识别
  13. linux内存条查询,linux查询内存命令
  14. mysql字符串索引与数字索引_×××数字和字符串数字的索引使用情况
  15. 自动化爬取网贷黑名单
  16. Array王锐大神力作:osg与PhysX结合系列内容——第5节 角色动画效果(上)
  17. APDL电磁仿真学习中可能会遇到的问题1
  18. NVCC Fatal
  19. NAPI之(一)——原理和实现
  20. 帝国CMS内容页调栏目名称和栏目地址

热门文章

  1. 猴子吃桃问题:一只小猴子摘了若干桃子,每天吃现有桃的一半多一个,到第10天时就只有一个桃子了,求原有多少个桃?请编程实现。(C++)(迭代法)
  2. 未明学院数据分析报告:金融专业如何找实习?数据分析告诉你整个金融实习市场全貌!
  3. 黑苹果双系统安装教程macOS High Sierra 10.13.x
  4. 北京地铁车站换乘示意图行动(2010年4月1日更新完整)
  5. win10如何更改c盘下的用户名用解决某些软件启动不了
  6. java项目生成多个条码_java – 生成随机条形码 – 一个设计问题
  7. PyTorch_Geometric 安装过程
  8. 一起弄个微信订阅号鉴黄机器人
  9. 【Android】时钟动态图标的定制化
  10. 【docker系列】docker API管理接口增加CA安全认证