redis源码分析之简单动态字符串sds

来源:互联网 发布:淘宝暴力退款怎么赚钱 编辑:程序博客网 时间:2024/05/03 07:19

Sds(Simple Dynamic String.简单动态字符串)是Redis底层所使用的字符串,它被用在几乎所有的redis模块中。


Sds在Redis中的用途主要有两个:

1.用于创建 stringObject 对象

2.在Redis内部作为char *的替代品。


1.实现字符串对象

Redis是一个键值对数据库,(key-value DataBase),数据库的值可以是字符串、集合、列表等多种类型的对象,而数据库的键则总是字符串对象。对于那些包含字符串值的字符串对象来说,每个字符串对象包含一个 sds 值。


2.将 sds 替代 C 默认的 char * 类型

C默认的char类型的字符串,功能单一,抽象层次低,并且不能高效的支持一些Redis操作(比如追加操作和长度计算操作),所以在Redis内部,绝大部分情况下会使用sds替代char*表示字符串。

在Redis中,客户端传入的协议内容、aof缓存内容、返回给客户端的回复等等,这些重要内容都是用sds类型保存的。


Redis中的字符串

在C语言中,字符串都是以'\0'结尾的。比如说 hello world 在c语言中表示为"hello wordl\0"

这种简单的字符串多数情况下可以满足要求,但是,它并不能高效的进行计算长度和追加的操作

1.计算长度操作需要使用 strlen()函数,复杂度为O(n)

2.如果追加N次,需要realloc N次

Redis中,这两种操作非常频繁,这两个简单的操作不能成为性能瓶颈。另外,Redis除了能处理字符串外,还要处理单字节的数组,以及服务器协议等内容,所以Redis的字符串还应该是二进制安全的。所以Redis采用Sds替代了C默认的char*字符串。sds既可以高效的进行计算长度和追加操作,还是二进制安全的。


sds的实现:

sds由两部分组成

typedef char *sds;struct sdshdr {    // buf 已占用长度    int len;    // buf 剩余可用长度    int free;    // 实际保存字符串数据的地方    char buf[];};
其中sds 是char* 的别名,而结构 sdshdr则保存了len、free和buf三个属性。

通过len,可以轻易的计算出字符串的长度(O(1))。

另一方面,通过对buf多分配一些空间,并使用free记录未使用的空间的大小,sdshdr可以让直行追加操作所需要的内存重分配次数大大减少。

当然sds的操作都会正确的更新len、free属性。


优化追加操作

因为sdshdr有free这个属性,所以在分配空间的时候,可以多分配一些空间,这样在下一次追加操作的时候,不一定需要再次分配空间,减少重新分配空间的次数。

比如我们在 最初一个字符串的 "hello world\0" sdshdr值如下,

struct sdshdr {    len = 11;    free = 0;    buf = "hello world\0";}
当我们Append的时候,Redis会重新分配空间,比如我们 append 一个字符串 again,只需要7个字节,但是Redis却分配了比所需空间大一倍的空间。机会分配出37个字节的空间,这样就多余了18个字节,那再下一次Append操作的时候,如果追加的字符串长度小于18个字节,就不用再次分配空间了,直到追加的时候,剩余空间不足够,才会在次重新分配空间,且又会分配2倍的空间。以此来减少重新分配空间的次数,但是多分配的空间,却不会回收,除非整个字符串所占用的空间都被销毁了。


总结:

1.Redis字符串表示为sds,不是C默认的char*

2.比起C字符串,sds有以下优势

2.1.方便计算字符串长度

2.2.追加操作更高效

2.3.二进制安全

3.sds为了优化追加操作,会多分配一些空间,但是多分配的内存不会主动回收,会浪费一点空间。



原创粉丝点击