Redis设计与实现-SDS

本文概述了Redis中的SimpleDynamicString(SDS)数据结构,介绍了其定义、与C字符串的区别,包括常数时间长度获取、防止缓冲区溢出、内存管理优化和二进制安全特性。SDS通过空间预分配和惰性空间释放策略提高效率,确保了数据处理的灵活性和性能。
摘要由CSDN通过智能技术生成

本文仅作为对《Redis设计与实现》一书的个人笔记与总结,仅供参考,如果有不正确的地方,欢迎指出。

1 SDS是什么

  SDS是简单动态字符串,是一种Redis自定义的抽象类型。说白了,就是Redis的字符串。为什么不用C传统的字符串呢?这个后续会解释,这里只需要知道SDS是什么就好。 

  总所周知,Redis是以键值对来存储数据的,其中健、各种数据类型(如set,zset)底层的数据存储方式就是用SDS来存储的。

  比如: SET msg “hello world”

  在这个命令中,msg是key,hello world是value,msg是一个字符串,hello world也是一个字符串。这两个字符串的存储形式就是SDS

2.1 SDS的定义

  这是书中对于sds的定义。

  这里大致有三个属性:len、free、buf

  · len:代表buf数组中已用字节的长度,图中为5,即代表存储了Redis这五个字符。

  ·free:代表buf数组中未用字节的数量,这里数组全都用完了,也就是0

  ·buf: 字节数组

  很显然,数组有6个元素,但是存了5个,但是free为0,这是因为最后用了空字符表示字符结束。

(注意,这里存储的是字符,但是我这些属性都是说的字节,一个字符对应一个字节,是一个意思,为什么要强调字节,这个后续会说。)

可能会有同学问,为什么有len这个属性了,还要用空字符代表结束呢?

  这是因为,C传统的字符串是用空字符结尾的,这样做的好处是一些传统的字符串函数是可以继续使用的。例如C中的pringtf,结尾是空字符串的话,Redis没有必要再为SDS写一个打印函数了。

  再来看一个例子:

  数组长度是11,free和len都是5,其中有一个空字符串代表字符串结束。

2.2 SDS和C字符串的区别

  首先C字符串是单纯一个字符数组,存N个字符就开N+1个char类型数组,结尾用空字符标识。

2.2.1常数复杂度获得字符串长度

  在C中,字符串长度要获取的话就要遍历一遍数组。流程如下。

  但是使用了SDS,获取字符串长度就变成了一个常数时间可以解决的事情了。

2.2.2 杜绝缓冲区溢出

  我们之前说了,C字符串是单纯的一个数组。

  那么假设内存空间中连续存储了两个C字符串 S1和S2。

  这看起来没什么问题,但如果程序员执行某些字符串的拼接函数,例如在Reids后面凭借 Cluster(注意,Cluster之前有一个空格),但是却没有为S1重新分配一个足够的空间,那就会导致S2的内容被修改。

  那你下次读取MongoDB的时候就会读取到Cluster....

  SDS的api中字符串拼接的函数会首先判断SDS空间是否足够,如果不够的话就会扩展空间,然后进行修改,这样就不会造成缓冲区溢出的问题。(我感觉这个不能太算一个优点,虽然我只学过C++,但是我个人感觉C也可以在api层面杜绝这个问题的)。

2.2.3 减少内存重分配

  在C中,你要拼接或者截断字符串的时候,一定会把新的字符串重新分配内存的某个区域,这就是内存重分配。(拼接很好理解,不申请内存的话可能造成缓冲区溢出,截断的话,如果你不重新分配内存,那么你不使用的那段空间就会无法被使用了,也就是内存泄漏)

  举个例子:我有一个字符串S=“YuanShen”,我要拼接一个“ QiDong!”,那么势必会重新分配一段内存,再把这个新字符串按序写入内存。学过操作系统的同学应该知道,内存管理是一个比较麻烦的事情,这个过程会比较耗时。一次两次还好,但是Redis是一个数据库!如果有个客户端天天在原神后面增加启动,单是内存管理就够Redis好受的了。

  因此C传统字符串不能满足需求。SDS中会有空间预分配策略惰性空间释放两个操作。

2.2.3.1 空间预分配

  如果有了解过,比如Java的List等数据结构,你哪怕插入一个数据,他也不会只给你存储一个数据的空间。例如,你只想往List插入一个数据,但是他会给你分配16个数据的空间,就是害怕你以后还要往里插入,这就是空间预分配,以后你要再往里插入的话,就会用已经分配好的空间。

  这个过程,实际上就是空间换时间。

  我们拿之前缓冲区溢出的例子。往“Redis”后拼接“ Cluster”

  这是之前的结构。

  这是之后的结构。

  

  很明显可以看到,除了插入“ Cluster”之后,len从5变成了13之外,free也变成了13!

  这就是空间预分配策略,提前给你分配好了一段空间,方便你后续使用。

  如果再拼接一个“ Tutorial”之后,就变成了。

  这实际上就是用空间换时间的一个策略。

2.2.3.2 惰性空间释放

  拼接我们解释了,那么截断呢?

  举个例子:

  “YuanShenQidong”这里是14个字符吧,如果我不想启动了,那么就变成了“YuanShen”这8个字符,后续的6个字符呢?暂时不释放这个空间,只需要free+6(14-8)即可,也就是说,如果后续我还想启动了,就不用重新分配内存了。

  当然,有同学可能会说,这样的话,如果一个很大的字符串截断成一个很小的字符串,后续又不再拼接了咋办?SDS也有相应的API释放,书中就没有细说了。

2.2.4 二进制安全

  SDS是用char数组存储数据,一个char存储一个字节的数据,例如我存‘A’这个字符,实际上就是把这个字节的值改成65。可如果我不存储字符了呢?我用一个char存储一个字节的数据,会发生什么问题?

  例如,buf数组中存了10个值,也就是80个二进制位。

  其中buf[0]存的是二进制的 

00000000

  这在ASCII码中就是空字符的意思。也就是说读到这里,就结束了,这合理吗?当然不合理

  因此,SDS中的api不会因为读到空字符就结束,他会用len这个属性判断是否结束。这样就能保证我可以存二进制数据了,例如:图像,音频。

  这也就是为什么我们说SDS的结构的时候,要强调是len和free是代表使用了和未使用的字节。

  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值