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. python内存分析

    安装 首先安装memory_profiler和psutil pip install memory_profiler pip install psutil 在需要分析的函数前面添加装饰器@profile ...

  2. 利用sql语句建立全国省市区三级数据库

    一.创建数据库zone CREATE DATABASE IF ONT EXISTS zone; 二.建立省级表并增加数据 DROP TABLE IF EXISTS `provinces`; CREAT ...

  3. [AHOI2008]紧急集合 / 聚会(LCA)

    [AHOI2008]紧急集合 / 聚会 题目描述 欢乐岛上有个非常好玩的游戏,叫做"紧急集合".在岛上分散有N个等待点,有N-1条道路连接着它们,每一条道路都连接某两个等待点,且通 ...

  4. [python 学习] lambda

    #!/usr/bin/python # -*- encoding:utf-8 -*- # lambda parameter_list: expression f = lambda x,y: x + y ...

  5. 利用xcode Build生成模拟器运行包

    真机只能运行.ipa包 模拟器上只能运行.app包 xcode中生成.app包步骤: 启动xcode IDE,打开gigold源码工程 [project]——[gigold]——[Basic]:修改V ...

  6. hdu 6035:Colorful Tree (2017 多校第一场 1003) 【树形dp】

    题目链接 单独考虑每一种颜色,答案就是对于每种颜色至少经过一次这种的路径条数之和.反过来思考只需要求有多少条路径没有经过这种颜色即可. 具体实现过程比较复杂,很神奇的一个树形dp,下面给出一个含较详细 ...

  7. ES6基本用法

    es6是JS(JavaScript)的下一个版本. 新增了let命令,用来声明变量.变量在第一个花括号内有用,先声明后引用.不允许重复声明.存在暂时性死区. const声明一个只读的常量.一旦声明,常 ...

  8. nginx之访问静态文件

    如何配置nginx,访问服务器上的静态文件? 1.在目录/data/interface_test下创建静态文件queryAppData.json,内容如下: 如何访问该文件呢? 2.修改/usr/lo ...

  9. springboot整合 thymeleaf 案例

    1.运行环境 开发工具:intellij idea JDK版本:1.8 项目管理工具:Maven 4.0.0 2.GITHUB地址 https://github.com/nbfujx/springBo ...

  10. [洛谷P3943]:星空(DP+最短路)

    题目传送门 题目背景 命运偷走如果只留下结果, 时间偷走初衷只留下了苦衷.你来过,然后你走后,只留下星空. 题目描述 逃不掉的那一天还是来了,小$F$看着夜空发呆.天上空荡荡的,没有一颗星星——大概是 ...