1. 动态字符串( simple dynamic string, SDS)

  在 Redis 中,当需要可以被重复修改的字符串时,会使用 SDS 类型 ,而不是 C 语言中默认的 C 字符串类型 。举个例子:

SET msg "Hello World"

  在这个语句中,Redis 会新建一个键值对,其中

  • key 为一个 字符串,对象的底层实现是一个保存着字符串 “msg” 的 SDS 对象。
  • value 为一个字符串,对象的底层实现是一个保存着字符串 “Hello World” 的 SDS 对象。

  如果是 key 对多个 value 的情况下, 如

RPUSH fruits "apple" "banana" "cherry"

  Redis 同样会新建一个键值对,其中

  • key 为一个 字符串,对象的底层实现是一个保存着字符串 “fruits” 的 SDS 对象。
  • value 为一个列表对象,包含着三个字符串对象,分别由三个 SDS 实现:第一个 SDS 保存着字符串 “apple” ,第二个 SDS 保存着字符串 “banana”,第三个 SDS 保存着字符串 “cherry”。

  SDS 对象除了作为保存数据库值的字符串之外,还用作缓冲区,比如 AOF 模块中的 AOF 缓冲区,客户端的输入缓冲区等等。

2. SDS 的定义

  

  SDS 的结构如上图所示,len 表示字符串长度,需要注意的是,SDS 的 buf 数组也和 C 字符串一样保留着最后的空字符,而它的 len 计算时不会加上这个空字符,所以 "Redis\0" 在这里 len 值为 5。由于封装的原因,这里的 ‘\0’ 实际上虽然存在,但是使用者是完全不知道这里还有一个空字符的存在的,而这里加上空字符的原因也是为了可以让 SDS 沿用 一些 C 字符串函数库的函数。比如可以直接用 printf("%s", s->buf); 来打印 SDS 的字符串。

3. SDS 和 C 字符串的区别

  • 传统的 C 字符串中,我们如果想要知道它的长度,需要遍历其一遍,直到遇到空字符为止,时间复杂度为 O(N)。而 SDS 可以直接取 len 的值,时间复杂度为 O(1)。
  • 由于没有字符串的长度,C 字符串很容易导致缓冲区溢出,访问越界问题。
  • C 字符串在使用的时候遇到空字符 ‘\0’ 便会认为字符串已经结束,一旦字符串中含有该字符则会导致字符串的异常截断,而 SDS 利用 len 字段可以防止这个情况。所以通过使用二进制安全的 SDS 可以存取任意数据。
  • 拼接字符串时,C 字符串需要内存重分配拓展数组的大小,防止缓冲区溢出,截断字符串时,C 字符串需要内存重分配释放截断部分,防止内存泄漏。而 Redis 为了提高性能,增加了一个未使用空间的字段,通过该字段实现了空间预分配和惰性空间释放两种优化策略。

空间预分配策略:每次 SDS 进行空间扩展的时候,程序除了修改当前已有的空间,还会为 SDS 分配额外的空间,这也是 free 字段的作用,记录额外空间的长度,额外空间的长度分配有两种策略:根据修改后 SDS 的长度,即 len 属性的值,len 小于 1 MB 时,分配和 len 一样大小的额外空间, len 大于 1MB 时,则 分配 1 MB 的额外空间。举个例子, SDS 为 13 个字节,扩展 2 个字节后,len 变成 15 个字节,这时会分配额外的 15 个字节,free = 15;这时实际的字符串长度为 15 + 15 + 1 = 31。

惰性空间释放策略:当 SDS 进行缩短字符串的操作时,不马上进行内存重分配,而是利用 free 字段将这些截断的字符串长度记录下来,举个例子,如下图所示的 SDS 结构

如果我们试图删除所有 “X” 和 “Y” 的字符, SDS 的结构会变成如下图所示。

可以看到所有 “X” 和 “Y” 的字符长度总计为 8 ,这里 free 的值也被修改为 8。这时如果再对字符串进行增长的操作,则可以防止进行内存重分配的操作,而直接使用前面 “节约” 下来的内存。

4. 总结

之前遇到过一个二进制数据存取的问题 ,将 protobuf 序列化成二进制存到 Redis 数据库,然后取出来的时候并没有正常取出整个字符串,导致数据取出来之后反序列化异常,对比了存取时字符串的长度,发现就是因为 '\0' 字符导致的,后面通过加上字符串的长度去取字符串解决了这个问题。

Redis设计与实现 -- 动态字符串对象(SDS)的更多相关文章

  1. Redis数据结构之简单动态字符串SDS

    Redis的底层数据结构非常多,其中包括SDS.ZipList.SkipList.LinkedList.HashTable.Intset等.如果你对Redis的理解还只停留在get.set的水平的话, ...

  2. Redis—简单动态字符串(SDS)

    目录 Redis-简单动态字符串(SDS) SDS的定义 SDS与C字符串的区别 1. 常数复杂度获取字符串长度: 2. 杜绝缓冲区溢出: 3. 减少修改字符串时带来的内存重分配次数 4. 二进制安全 ...

  3. Redis 数据结构之简单动态字符串SDS

    几个概念1:key对象 数据库存储键值对的键,总是一个字符串对象.2:value对象 数据库存储键值对的值,可以是字符串对象,list对象,hash对象,set对象,sorted set对象.    ...

  4. redis之简单动态字符串(SDS)

    O(N):时间复杂度 N的值越大 时间复杂度随N的平方增大 O(1):就是说N很大的时候,复杂度基本不增长了.基本就是常量了. 在Redis数据库里 包含字符串值的键值对 在底层都是由SDS实现的. ...

  5. Redis 设计与实现:字符串 SDS

    本文的分析没有特殊说明都是基于 Redis 6.0 版本源码 redis 6.0 源码:https://github.com/redis/redis/tree/6.0 在 Redis 中,字符串都用自 ...

  6. Redis核心原理-简单动态字符串SDS

    SDS简介 Redis是C语言编写的,但没有使用c语言的字符串结构,而是自己实现了一套简单动态字符串 simple dynamic string 简称SDS,SDS兼容C语言的字符串类型,原理类似Ja ...

  7. Redis中的简单动态字符串

    Redis没有直接使用C语言传统的字符串表示(以空字符结尾的字符数组,以下简称C字符串),而是自己构建了一种名为简单动态字符串(simple dynamic string,SDS)的抽象类型,并将SD ...

  8. redis 笔记01 简单动态字符串、链表、字典、跳跃表、整数集合、压缩列表

    文中内容摘自<redis设计与实现> 简单动态字符串 1. Redis只会使用C字符串作为字面量,在大多数情况下,Redis使用SDS(Simple Dynamic String,简单动态 ...

  9. 简单动态字符串(SDS)

    SDS 前提:在redis中,C字符串只会作为字符串字面量用在一些无须对字符串进行修改的地方,比如打印日志: redisLog(REDIS_WARNING, “Redis is ready to ex ...

随机推荐

  1. AOP拦截日志类,抛异常:java.lang.IllegalStateException: It is illegal to call this method if the current request is not in asynchronous mode

    AOP的日志拦截类中,抛出异常: java.lang.IllegalStateException: It is illegal to call this method if the current r ...

  2. ltp-ddt eth_switch_config学习

    # @name ALE Table test using SWITCH-CONFIG # @desc Checks default entries in ALE table and verifies ...

  3. 美国的科技公司是如何使用加密的DNS

    加密设备和“以隐私为中心”的提供商之间的DNS流量可以阻止某人窥探您的浏览器所指向的位置,或者使用DNS攻击将其发送到其他地方. 该网络中立性的死亡和法规对互联网服务供应商如何处理客户的网络流量的松动 ...

  4. 18 StringBuilder类型有何作用

  5. 如何将DynamoDB的数据增量迁移到表格存储

    Amazon DynamoDB是一个完全托管的NoSQL数据库服务,可以提供快速的.可预期的性能,并且可以实现无缝扩展.由于DynamoDB并可以根据实际需求对表进行扩展和收缩,这个过程既不需要停止对 ...

  6. Linux CentOS7 安装docker

    Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口 ...

  7. 如何扩展 Create React App 的 Webpack 配置

    如何扩展 Create React App 的 Webpack 配置  原文地址https://zhaozhiming.github.io/blog/2018/01/08/create-react-a ...

  8. (转)使用windows server2008 创建 Hyper-V虚拟机

    转:https://jingyan.baidu.com/article/7c6fb42833ad4980652c904f.html Hyper-v是微软提供的虚拟机,利用server 2008搭建hy ...

  9. 原来在UNITY中使用system.io下的所有函数都可以用相对路径 : Assets/xx

    代码如下图,这样就不用在绝对路径和相对路径之间不断转换了. 想要得到绝对路径时就傅 Application.dataPath  + xxx using System.Collections; usin ...

  10. jmeter 命令行运行与生成报告

    一.     使用命令行方式运行Jmeter 1.1 为什么 使用GUI方式启动jmeter,运行线程较多的测试时,会造成内存和CPU的大量消耗,导致客户机卡死. 所以正确的打开方式是在GUI模式下调 ...