Redis设计与实现 -- 动态字符串对象(SDS)
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)的更多相关文章
- Redis数据结构之简单动态字符串SDS
Redis的底层数据结构非常多,其中包括SDS.ZipList.SkipList.LinkedList.HashTable.Intset等.如果你对Redis的理解还只停留在get.set的水平的话, ...
- Redis—简单动态字符串(SDS)
目录 Redis-简单动态字符串(SDS) SDS的定义 SDS与C字符串的区别 1. 常数复杂度获取字符串长度: 2. 杜绝缓冲区溢出: 3. 减少修改字符串时带来的内存重分配次数 4. 二进制安全 ...
- Redis 数据结构之简单动态字符串SDS
几个概念1:key对象 数据库存储键值对的键,总是一个字符串对象.2:value对象 数据库存储键值对的值,可以是字符串对象,list对象,hash对象,set对象,sorted set对象. ...
- redis之简单动态字符串(SDS)
O(N):时间复杂度 N的值越大 时间复杂度随N的平方增大 O(1):就是说N很大的时候,复杂度基本不增长了.基本就是常量了. 在Redis数据库里 包含字符串值的键值对 在底层都是由SDS实现的. ...
- Redis 设计与实现:字符串 SDS
本文的分析没有特殊说明都是基于 Redis 6.0 版本源码 redis 6.0 源码:https://github.com/redis/redis/tree/6.0 在 Redis 中,字符串都用自 ...
- Redis核心原理-简单动态字符串SDS
SDS简介 Redis是C语言编写的,但没有使用c语言的字符串结构,而是自己实现了一套简单动态字符串 simple dynamic string 简称SDS,SDS兼容C语言的字符串类型,原理类似Ja ...
- Redis中的简单动态字符串
Redis没有直接使用C语言传统的字符串表示(以空字符结尾的字符数组,以下简称C字符串),而是自己构建了一种名为简单动态字符串(simple dynamic string,SDS)的抽象类型,并将SD ...
- redis 笔记01 简单动态字符串、链表、字典、跳跃表、整数集合、压缩列表
文中内容摘自<redis设计与实现> 简单动态字符串 1. Redis只会使用C字符串作为字面量,在大多数情况下,Redis使用SDS(Simple Dynamic String,简单动态 ...
- 简单动态字符串(SDS)
SDS 前提:在redis中,C字符串只会作为字符串字面量用在一些无须对字符串进行修改的地方,比如打印日志: redisLog(REDIS_WARNING, “Redis is ready to ex ...
随机推荐
- Tensorflow学习笔记3:卷积神经网络实现手写字符识别
# -*- coding:utf-8 -*- import tensorflow as tf from tensorflow.examples.tutorials.mnist import input ...
- 5-基于TMS320C6678+XC7K325T的6U CPCIe高性能处理平台
基于TMS320C6678+XC7K325T的6U CPCIe高性能处理平台 一.板卡概述 本板卡系自主研发,基于CPCI 6U架构,符合CPCI2.0标准.采用 DSP TMS320C66 ...
- 树——sum-root-to-leaf-numbers(根到叶节点数字之和)
问题: Given a binary tree containing digits from0-9only, each root-to-leaf path could represent a numb ...
- Oracle 索引数据字典、基于函数的索引
user_indexes 字典视图包含了索引名和唯一性, user_ind_columns视图包含了索引名.表名.以及列名 dba_indexes dba_ind_columns 同理 select ...
- Flutter-Boxdecoration邊框線, 圓角
decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), border: Border( top: BorderSide( ...
- gitignore 忽略文件
*.project*.prefs*.classpath*.gitignore#ignore thumbnails created by windowsThumbs.db#Ignore files bu ...
- Cobaltstrike系列教程(三)-beacon详解
0x000--前文 Cobaltstrike系列教程(一)-简介与安装 Cobaltstrike系列教程(二)-Listner与Payload生成 heatlevel 0x001-Beacon详解 1 ...
- Dubbo学习-4-dubbo简单案例-1
模拟一个需求,通过dubbo实现RPC调用: 这里用户服务模块的查询用户地址的功能,就是一个服务提供者,而订单服务模块的创建订单模块就是一个服务消费者: 1. 创建服务提供者的maven工程:user ...
- unique && stl的全排列
stl的全排列: 看代码. #include<iostream> #include<cstdio> #include<algorithm> #include< ...
- Cluster基础(一):配置iSCSI服务、编写udev规则、配置并访问NFS共享、部署Multipath多路径环境
一.配置iSCSI服务 目标: 本案例要求先搭建好一台iSCSI服务器,并将整个磁盘共享给客户端: 虚拟机添加新的磁盘 将新添加的磁盘分区并创建两个逻辑卷 逻辑卷名称分别为:/dev/myvg/isc ...