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

  在 Redis 里面, C 字符串只会作为字符串字面量(string literal), 用在一些无须对字符串值进行修改的地方, 比如打印日志.

  当 Redis 需要的不仅仅是一个字符串字面量, 而是一个可以被修改的字符串值时, Redis 就会使用 SDS 来表示字符串值

1.SDS的定义

struct sdshdr {

    // 记录 buf 数组中已使用字节的数量, 不包括 '\0' 的长度
// 等于 SDS 所保存字符串的长度
int len; // 记录 buf 数组中未使用字节的数量
int free; // 字节数组,用于保存字符串
char buf[]; };

  buf [ ] 除了保存字符串的字符外, 还会在末尾保存一个空字符 '\0' , 空字符不计算在 len 属性之中.

  遵循空字符结尾的好处是可以重用一部分C字符串的函数.

2.SDS与C字符串的区别

2.1 常数复杂度获取字符串长度

  C字符串不记录自身的长度信息, 获取字符串长度时会遍历字节数组, 直到遇到空字符为止. 复杂度为 O(N)

  SDS直接通过 len 属性获取字符串长度. 复杂度为O(1)

2.2 杜绝缓冲区溢出

  C字符串不记录自身长度, 修改字符串时不会判断本身是否拥有足够的内存空间, 当内存空间不足时, 则会造成缓冲区的溢出.

  SDS对字符串进行修改时,先检查内存空间是否满足修改的需要, 若不满足, 则自动扩展SDS的内存空间. 所以使用SDS既不需要手动修改内存空间的大小, 也不会出现缓冲区溢出的情况.

2.3 空间预分配

  第一次创建字符串对象时, SDS不会分配冗余空间, 即 len = 0

  当SDS的API修改SDS时, 则会为其分配冗余空间.

  1. 当修改后的SDS的 len 属性小于1MB时, 则为其分配和 len 同样大小的冗余空间, 即 free = len, 此时 buf [ ] 的实际长度 = len(实际长度) + free(冗余空间) + 1(空字符)
  2. 当修改后的SDS的 len 属性大于等于1MB时, 则为其分配1MB的冗余空间.  buf [ ] 的实际长度 = len(实际长度) + free(1MB) + 1(空字符)

2.4 惰性空间释放

  SDS的API缩短SDS的字符串时, 不会立即使用内存分配回收缩短后多出来的字节, 而是记录在 free 属性中, 并等待将来使用.

2.5 二进制安全

  C字符串中的字符必须符合某种编码(比如ASCII),并且除了字符串的末尾之外,字符串里面不能包含空字符,否则最先被程序读入的空字符将被误认为是字符串结尾,这些限制使得C字符串只能保存文本数据,而不能保存像图片、音频、视频、压缩文件这样的二进制数据。

  SDS的API都是二进制安全的.所有SDS API都会以处理二进制的方式来处理SDS存放在buf数组里的数据,程序不会对其中的数据做任何限制、过滤、或者假设,数据在写入时是什么样的,它被读 取时就是什么样。

  这也是我们将SDS的buf属性称为字节数组的原因——Redis不是用这个数组来保存字符,而是用它来保存一系列二进制数据。

3. 字符串的不同编码方式

  传送门 : Redis 数据编码方式详解

  为什么会有不同的编码方式,为了解释这种现象,我们首先来了解一下 Redis 对象头结构体,所有的 Redis 对象都有下面的这个结构头:

struct RedisObject {
int4 type; // 4 bits
int4 encoding; // 4 bits
int24 lru; // 24 bits
int32 refcount; // 4 bytes
void *ptr; // 8 bytes,64-bit system
} robj;

  不同的对象具有不同的类型 type(4bit),同一个类型的 type 会有不同的存储形式 encoding(4bit),为了记录对象的 LRU 信息,使用了 24 个 bit 来记录 LRU 信息。

  每个对象都有个引用计数,当引用计数为零时,对象就会被销毁,内存被回收。ptr 指针将指向对象内容 (body) 的具体存储位置。

  这样一个 RedisObject 对象头需要占据 16 字节的存储空间。

3.1 embstr

  从Redis 3.0版本开始字符串引入了EMBSTR编码方式,长度小于OBJ_ENCODING_EMBSTR_SIZE_LIMIT(39)的字符串将以EMBSTR方式存储。

  EMBSTR方式的意思是 embedded string ,字符串的空间将会和redisObject对象的空间一起分配,两者在同一个内存块中。

  Redis中内存分配使用的是jemalloc,jemalloc分配内存的时候是按照 8,16,32,64 作为chunk的单位进行分配的。

  为了保证采用这种编码方式的字符串能被jemalloc分配在同一个chunk中,该字符串长度不能超过64,

  故字符串长度限制OBJ_ENCODING_EMBSTR_SIZE_LIMIT = 64 - sizeof('0') - sizeof(robj)为16 - sizeof(struct sdshdr)为8 = 39。

  采用这个方式可以减少内存分配的次数,提高内存分配的效率,降低内存碎片率。

3.2 raw

  从len字段可以判断并不不依赖于'0',故可以用与保存二进制对象。

  从free字段可以判断其空间分配是采用预分配的方式,避免字符串修改时频繁分配释放内存。

3.3 int

  INT编码方式以整数保存字符串数据,仅限能用long类型值表达的字符串。

  当robj中的LRU值没有意义的时候(实例没有设置maxmemory限制或者maxmemory-policy设置的淘汰算法中不计算LRU值时),

  0-10000之间的OBJ_ENCODING_INT编码的字符串对象将进行共享。

Redis---SDS(简单动态字符串)的更多相关文章

  1. Redis的简单动态字符串实现

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

  2. 小白的Redis学习(一)-SDS简单动态字符串

    本文为读<Redis设计与实现>的记录.该书以Redis2.9讲解Redis相关内容.请注意版本差异. Redis使用C语言实现,他对C语言中的char类型数据进行封装,构建了一种简单动态 ...

  3. Redis数据类型之SDS简单动态字符串

    一,简单的动态字符串 1,Redis自己构建了一种名为简单动态字符串的抽象类型,并将SDS用作Redis的默认字符串表示, 2,在redis的数据库里面,包含字符串值的键值对在底层都是由SDS实现的 ...

  4. 深入理解Redis 数据结构—简单动态字符串sds

    Redis是用ANSI C语言编写的,它是一个高性能的key-value数据库,它可以作用在数据库.缓存和消息中间件.其中 Redis 键值对中的键都是 string 类型,而键值对中的值也是有 st ...

  5. 【Redis】简单动态字符串SDS

    C语言字符串 char *str = "redis"; // 可以不显式的添加\0,由编译器添加 char *str = "redis\0"; // 也可以添加 ...

  6. 深入理解Redis之简单动态字符串

    目录 SDS SDS与C字符串的区别 SDS获取字符串长度复杂度为O(1),C字符串为O(N) SDS杜绝了缓存区溢出 减少修改字符串时带来的内存重分配次数 二进制安全 Redis没有直接使用C语言传 ...

  7. 关于redis中SDS简单动态字符串

    1.SDS 定义 在C语言中,字符串是以’\0’字符结尾(NULL结束符)的字符数组来存储的,通常表达为字符指针的形式(char *).它不允许字节0出现在字符串中间,因此,它不能用来存储任意的二进制 ...

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

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

  9. sds(简单动态字符串) 内存预分配优化策略

    * 1024 , 也就是说. 当大小小于 1MB 的字符串运行追加操作时,sdsMakeRoomFor 就为它们分配多于所需大小一倍的空间: 当字符串的大小大于 1MB . 那么 sdsMakeRoo ...

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

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

随机推荐

  1. MySQL查找SQL耗时瓶颈 SHOW profiles

    http://blog.csdn.net/k_scott/article/details/8804384 1.首先查看是否开启profiling功能 SHOW VARIABLES LIKE '%pro ...

  2. vue组件实现查看大图效果

    使用的index.vue代码 <template> <img :src="imgUrl" @click="clickImg($event)"& ...

  3. centos 7 搭建pip源

    一.安装pip2pi工具: pip install pip2pi 或编译: git clone https://github.com/wolever/pip2pi cd pip2pi python s ...

  4. Codeforces 1107 简要题解

    文章目录 A题 B题 C题 D题 E题 F题 G题 传送门 A题 传送门 题意简述:问你能不能把一个数字串切成若干块,使得切出来的kkk个数k≤2k\le2k≤2满足a1<a2<...&l ...

  5. 2018.11.30 bzoj3230: 相似子串(后缀数组)

    传送门 后缀数组入门题. 建立正反两个后缀数组算就行了. 代码: #include<bits/stdc++.h> #define ri register int using namespa ...

  6. Opencv(3):基本数据类型

    1.比较简单的原子类型 结构 成员 意义 CvPoint int x,y 图像中的点 CvPoint2D32f float x,y 二维空间中的点 CvPoint3D32f float x,y,z 三 ...

  7. Winform自定义表单(转)

    出处:http://www.newlifex.com/showtopic-167.aspx 好吧,附件真的损坏了,原始代码我也没有了,再提取我也没精力了,不好意思,哪位之前下过可以重发一遍吗?不过即使 ...

  8. 安装kafka 集群 步骤

    1.下载 http://mirror.bit.edu.cn/apache/kafka/2.1.0/kafka_2.11-2.1.0.tgz 2.解压  tar -zxvf kafka_2.11-2.1 ...

  9. Win7 VS2017简单编译FFMPEG播放器FFPlay

    Win平台简单编译FFPlay播放器,顺便纪念下雷霄骅,一年前刚学FFMPEG时还看过他的博客,昨晚再次搜FFMPEG文章时才知道人已经走了... 做成了视频放到B站 https://www.bili ...

  10. idea的maven项目不知道为啥下载不下来jar包,看本地仓库只是下载了一下xml文件,没有jar包问题

    确认了私服上的jar包能直接下载下来,而且jar包是正常的:但是就是不能通过idea下载,通过idea下载到本地仓库的只生成了.lastUpdated文件,检查了setting.xml文件.网络,私服 ...