一、关于SDS

  Redis没有直接使用C语言传统的字符串表示(也就是以空字符结尾的字符数组),而是自己构建了一种名为简单字符串的抽象类型(Simple Dynamic String),并将SDS用作了Redis默认的字符串表示。在Redis3.0之前,对于sds的结构定义如下:

struct sdshdr {// 记录buf数组中已使用字节的数量,等于SDS所保存字符串长度unsigned int len; // 记录buf数组中未使用字节的数量unsigned int free;// 字节数组,用于保存字符串char buf[];
};

而在3.2之后的版本结构如下所示:

/* Note: sdshdr5 is never used, we just access the flags byte directly.* However is here to document the layout of type 5 SDS strings. */
struct __attribute__ ((__packed__)) sdshdr5 {unsigned char flags; /* 3 lsb of type, and 5 msb of string length */char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 {uint8_t len; /* used */uint8_t alloc; /* excluding the header and null terminator */unsigned char flags; /* 3 lsb of type, 5 unused bits */char buf[];
};
struct __attribute__ ((__packed__)) sdshdr16 {uint16_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 {uint32_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 {uint64_t len; /* used */uint64_t alloc; /* excluding the header and null terminator */unsigned char flags; /* 3 lsb of type, 5 unused bits */char buf[];
};

其实只是做了一下优化,为了节省空间,定义了这五种类型。

struct __attribute__ ((__packed__))

表示去掉对其填充,之所以去掉对其填充,是因为获取当前的SDS的类型只需要直接获取flags就可以了,若进行对齐填充,由于 Padding 的存在,我们在不同的系统中不知道退多少才能获得flags,并且我们也不能将 sds 的指针指向flags,这样就无法兼容 C 语言的函数了。

在Redis数据库中,包含字符串值的键值对子在底层都是由SDS实现的,比如执行命令

redis> SET lordky "this is test value"
OK

在Redis中将会创建一个新的键值对,Key将会是一个保存着“lordky”字符串的SDS。而Value将会是一个保存着“this is test value”的SDS。

如图所示,SDS中free属性为0,表示没有分配任何未使用的空间,length为15表示这个SDS保存了一个15个字节长度的字符串。buf数组就是一个char类型的数组,实际保存着字符串,而最后也和C语言的字符串一样,以‘\0’结尾。这样做SDS可以直接重用一部分C语言字符串函数库里面的函数。虽然如此,但是SDS判断字符串是否结束并不是以空字符来进行判断的,正如上图所示,SDS的buf数组中间也有空字符,这是由于SDS是采用length的值来进行判断的。

二、为什么使用SDS

  1. 获取字符串长度时间复杂度为O(1)
    我们根据SDS的数据结构可以看出,要获取字符串的长度直接获取lenth值就可以了,但是针对于C语言的字符串来说,就需要遍历整个字符串数组才能获取到其长度。这样确保了获取字符串长度的工作不会称为Redis性能的瓶颈。
  2. 杜绝缓冲区溢出
    C语言的字符串还有一个问题就是容易造成缓冲区溢出。比如,在内存中,紧邻着两个C字符串s1、s2,其中s1保存着“lordky”,s2保存着"test",如图所示:

    当我们执行stract(s1," king")的时候,s1的内容变成了“lordky king”,但是,如果我们在执行stract命令之前没有为s1分配足够的空间,当执行stract命令后,s1的数据将会溢出到s2中。变成如下情况:

    但是SDS的空间分配策略完全杜绝了发生缓冲区溢出的可能性,当SDSAPI需要对SDS进行修改时,API会先检查SDS的空间是否满足修改所需的要求,如果不满足的话,API会自动将SDS的空间扩展至执行修改所需的大小,然后才执行实际的操作,所以,SDS既不需要手动修改SDS的空间大小,也不会出现缓冲区溢出的问题。redis3.0自动扩容的代码如下:
sds sdsMakeRoomFor(sds s, size_t addlen) {struct sdshdr *sh, *newsh;size_t free = sdsavail(s);size_t len, newlen;// 当空闲空间大于新增空间的时候,直接返回,不需要扩容if (free >= addlen) return s;len = sdslen(s);   // 获取当前长度 sh = (void*) (s-(sizeof(struct sdshdr)));newlen = (len+addlen);    // 新的长度// #define SDS_MAX_PREALLOC (1024*1024)该定义在sds.h中if (newlen < SDS_MAX_PREALLOC)// 也就是当新长度小于1MB的时候直接double扩容newlen *= 2;else// 当新长度大于1MB的时候直接用新长度+1MBnewlen += SDS_MAX_PREALLOC;newsh = zrealloc(sh, sizeof(struct sdshdr)+newlen+1);if (newsh == NULL) return NULL;newsh->free = newlen - len;return newsh->buf;
}
  1. 减少修改字符串时带来的内存重分配次数
    对于C字符串来说,底层实现总是一个N+1个字符长的数组。每次增加或者缩短一个C语言字符串,程序都总要对保存这个C字符串的数组进行一次内存重分配操作:
      如果进行曾长字符串,那么在执行操作之前,程序就需要先通过内存重分配来扩展底层数组的空间大小,如果略过这一步就会出现前面所提到的缓冲区溢出的情况
      如果程序执行的是缩短字符串的操作,那么在执行操作之后程序就需要通过内存分配来释放字符串不再使用的那部分空间,如果不进行这一步就会产生内存泄露。

    针对SDS,buf数组的长度不一定就是字符长度数量加一,数组里面可以包含未使用的字节,而这些字节的数量就有SDS的free属性记录。SDS就通过未使用空间,实现了空间预分配惰性空间释放两种优化策略

    针对空间预分配:
    空间预分配用于优化SDS字符串增长的操作,也就是SDS的API对一个SDS进行修改,并且需要对SDS进行空间扩展的时候,程序不仅会为SDS分配所必须要的空间,还会为SDS分配额外的未使用的空间。具体逻辑正如上面自动扩容代码中所述一样,当新长度小于1MB的时候直接double扩容,当新长度大于1MB的时候直接用新长度+1MB

    惰性空间释放:
    惰性空间释放用于优化SDS的字符串缩短的操作,当SDS需要缩短保存的字符串的时候,程序不会立即使用内存重分配来回收缩短后多出来的字节,而是使用free属性将这些字节的熟练记录下来,等待将来使用。这样就避免的重复对一个SDS进行操作所产生的内存分配开销。当然,SDS也提供了相应真正释放不用内存空间的方法sdsRemoveFreeSpace以避免内存泄露的问题

  2. 二进制安全
      针对于C语言的字符串,除了字符串末尾之外,字符串里面不能包含空字符串,否则最先被程序读入的空字符将会被误认为是字符串结尾,这些限制使得C字符串只能保存文本数据,而不能保存类似图片、音频、视频等二进制数据。
      但是对于SDS来说,正如我们开始提到的,SDS判断字符串是否结束并不是以空字符来进行判断的,而是采用SDS的len属性来进行判断的。而Redis为了可以适应各种不同的使用场景(因为很多时候需要保存二进制数据),SDS的API都是二进制安全的。针对buf字节数组,Redis不是用这个数组来保存字符,而是用它来保存一系列的二进制数据。

Redis数据结构(一)SDS相关推荐

  1. Redis 数据结构 :SDS、链表、字典、跳表、整数集合、压缩列表

    文章目录 SDS 结构分析 内存策略 空间预分配 惰性空间释放 总结 链表 结构分析 总结 字典 结构分析 rehash 渐进式rehash 总结 跳表 结构分析 总结 整数集合 结构分析 升级 降级 ...

  2. Redis 对象的数据结构原理 - SDS、Inset、Dict、ZipList、QuickList、SkipList、RedisObject

    Redis 数据结构 1. SDS Redis 是用 C 语言写的,但是对于 Redis 的字符串,却不是 C 语言中的字符串(即以空字符'\0'结尾的字符数组),它是自己构建了一种名为 简单动态字符 ...

  3. Redis 数据结构-字典源码分析

    2019独角兽企业重金招聘Python工程师标准>>> 相关文章 Redis 初探-安装与使用 Redis 数据结构-字符串源码分析 本文将从以下几个方面介绍 前言 字典结构图 字典 ...

  4. 为了拿捏 Redis 数据结构,我画了 40 张图

    Redis 为什么那么快? 除了它是内存数据库,使得所有的操作都在内存上进行之外,还有一个重要因素,它实现的数据结构,使得我们对数据进行增删查改操作时,Redis 能高效的处理. 因此,这次我们就来好 ...

  5. 【带你重拾Redis】Redis数据结构及使用场景

    Redis数据结构 Redis有着非常丰富的数据结构,这些数据结构可以满足非常多的应用场景, 如果对这些数据结构有一个比较清晰的认知,使用Redis也会更加得心应手. Redis主要支持以下数据结构: ...

  6. redis 数据结构

    2019独角兽企业重金招聘Python工程师标准>>> redis对象 redis 数据结构 字符串对象  SDS(简单动态字符串) 列表对象  压缩列表(ziplist) 或 链表 ...

  7. Redis 数据结构与内存管理策略(上)

    Redis 数据结构与内存管理策略(上) 标签: Redis Redis数据结构 Redis内存管理策略 Redis数据类型 Redis类型映射 Redis 数据类型特点与使用场景 String.Li ...

  8. redis value多大会影响性能_选择合适Redis数据结构,减少80%的内存占用

    前言 redis作为目前最流行的nosql缓存数据库,凭借其优异的性能.丰富的数据结构已成为大部分场景下首选的缓存工具. 由于redis是一个纯内存的数据库,在存放大量数据时,内存的占用将会非常可观. ...

  9. long 转为string_面试必问 Redis数据结构底层原理String、List篇

    点击关注上方"Java大厂面试官",第一时间送达技术干货. 阅读文本大概需要 8 分钟. 前言 今天来整理学习下Redis有哪些常用数据结构,都是怎么使用的呢?首先看下全局存储结构 ...

  10. 你需要知道的那些 redis 数据结构(前篇)

    戳蓝字"CSDN云计算"关注我们哦! 作者 | 饿了么物流技术团队 来源 | CSDN 企业博客 redis 对于团队中的同学们来说是非常熟悉的存在了,我们常用它来做缓存.或是实现 ...

最新文章

  1. 羊皮卷的实践-第二十五章
  2. 我爱计算机视觉干货集锦分类汇总(2019年3月9日)
  3. 阿里 20 年,逍遥子宣告「全面迈入数字经济时代」
  4. VMware SDS 之一:什么是VSAN??
  5. 微服务架构实战:Swagger规范RESTful API
  6. Viewbox在UWP开发中的应用
  7. C语言程序设计教材九斗验证,C语言实验报告参考答案(原)
  8. 厦门高考成绩查询2021,2021厦门市地区高考成绩排名查询,厦门市高考各高中成绩喜报榜单...
  9. 长截图、识别图片里的文字,不用装其它软件!
  10. 消费者行为学的典型营销案例
  11. 使用Maxima求解常微分方程~
  12. 喜羊羊与灰太狼java_喜羊羊与灰太狼之懒洋洋风波
  13. 中国有多少个省?多少个地级市?多少个县?多少个乡镇?一张统计表全部搞定。多关注民政部的信息吧^_^
  14. C++课后作业 6.教材习题5_7:利用静态变量统计小猫Cat的数量
  15. 图学习02—图神经网络的发展
  16. C++ system()函数的常用用法 (史上最详细)
  17. 你只需要做你一个让世界随之起舞的枭雄
  18. 二维码原理及生成示例
  19. 从0开始搭建一个自己的问卷系统
  20. 人工智能三大学派和主要数学函数

热门文章

  1. OSPF详解(六)特殊区域类型
  2. 除了 P 站,程序员摸鱼还喜欢上哪些网站?
  3. pyhanlp常用功能简单总结
  4. 私有云的优缺点_私有云服务器的优缺点
  5. LoRa无线模块的优势
  6. openvswitch console输出
  7. latex入门(一)——latex网站overleaf
  8. 李宏毅svm_CAA | 【智能自动化学科前沿讲习班第1期】国立台湾大学(位于中国台北)李宏毅教授:Anime Face Generation...
  9. deviceOrientation简介
  10. 十、Linux开发板控制LED灯设备