Redis数据结构 -- 简单动态字符串

244 阅读5分钟

​ 本篇主要对redis的数据结构进行总结,主要包括SDS(simple dynamic string)的实现的介绍、为什么Redis采用SDS而不是C字符串。

1、SDS的结构实现及定义

  • free属性 -- 记录buf数组中未使用字节的数量
  • len属性 -- 记录buf数组中已使用字节的数量 == SDS所保存的字符串的长度
  • buf属性 -- 字节数组,用于保存字符串(注:最后一个字节为 \0,遵循C字符串以空字符结尾的惯例,方便直接调用C字符串的库函数)。

2、为什么Redis采用SDS而不是C字符串?

注:C字符串使用长度为N+1的字符数组来表示长度为N的字符串,并且最后一个字符总数空字符串 \0。

Redis使用SDS而不是简单的使用C字符串有如下的好处:

  1. 常数级的获取字符串的长度。
  2. 杜绝缓冲区溢出
  3. 减少修改字符串时带来的内存重分配次数。
  4. 二进制安全
  5. 兼容部分C字符串函数

接下就上述好处做具体分析:

1、常数级的获取字符串的长度

​ 因为C字符串本身不记录自身的长度,而需要每次遍历字符串来获取,其时间复杂度为O(n),而SDS只需通过属性len即可以获取,时间复杂度为O(1)。

2、杜绝缓冲区溢出

​ 由于C字符串不记录自身长度带来的另外一个问题就是----容易造成缓冲区溢出(buffer overflow)。

而SDS的空间分配策略完全杜绝了发生缓冲区溢出的可能性:

​ 当SDS的API对SDS进行修改时,API会先检查SDS的空间是否满足修改需求,如果不满足,API会自动将SDS的空间扩展至执行修改所需大小,然后执行实际的修改操作,所以SDS不需要手动修改SDS的大小,也不会造成缓冲区溢出的问题。

​ 同时,sdscat不仅对SDS进行拼接操作,还会额外分配一定的未使用空间即free的值。

3、减少修改字符串时带来的内存重分配次数

首先我们看一下C字符串的内存重分配策略:

  1. 增长字符串的操作,比如拼接(append),那么在执行这一步之前,程序需要通过内存重分配来扩展底层数组的大小(注:忘了这一步则会造成 --缓冲区溢出。)
  2. 缩短字符串的操作,比如截断(trim),那么在执行这一步之前,程序需要通过内存重分配来释放字符串不使用的那部分空间(注:忘了这一步则会造成 --内存泄漏。)

同时,内存重分配涉及复杂的算法,并且可能需要执行系统调用,所以通常比较耗时。这对于经常被用于速度要求严苛、数据修改频繁Redis数据库而言,是无法接受的。

​ 为了避免这种缺陷,SDS通过未使用空间解除了字符串长度和底层数组长度之间的关联:buf数组长度不一定是字符串数量+1,数组内可以包含未使用的字节 -- free属性记录。

​ 通过未使用空间,SDS实现了 空间预分配惰性空间释放两种优化策略。

  • 空间预分配

    主要用于优化 SDS的字符串增长操作:当SDS的API需要对SDS进行空间扩展时,程序不仅会为SDS分配修改时所必须的空间,还会分配额外的未使用空间。

    1. 当SDS的长度 < 1MB时 ---> free = len
    2. 当SDS的长度 > 1MB时 ---> free = 1MB

    通过空间预分配策略:Redis可以减少连续执行字符串增长操作所有的内存重分配次数。

  • 惰性空间释放

    主要用于优化SDS的字符串缩短操作:当SDS的API需要缩短SDS保存的字符串时,程序并不马上使用内存重分配来回收缩短后多出来的字节,而是使用free属性将这些字节记录下来,并等待将来使用。

    通过惰性空间释放策略:SDS避免了缩短字符串时所需的内存重分配操作,并未将来可能有的增长操作优化。

    同时,SDS也提供了相应的API,让我们可以在有需要时候,真正的释放SDS的未使用空间,所以不用担心会造成内存浪费。

4、二进制安全

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

​ 而SDS的buf区不会对数据做任何限制、过滤等操作,写入时什么样,读取时便是怎么样。

​ 这是因为:SDS使用len属性值而不是空字符串\0来判断字符串是否结束。

5、兼容部分C字符串函数

​ 虽然SDS的API是二进制安全的,但是他们一样遵循C字符串以空字符串\0结尾,这使得SDS可以重用一部分<string.h>的函数。

本篇对《Redis设计与实现》一书的总结,感谢大家支持~~~~~~~~~~~~~~~~~~~~