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

概述

众所周知,Redis是使用C语言实现,那么Redis中字符串是否直接使用C字符串呢?答案是:否。
Redis构建了一种简单动态字符串(Simple Dynamic String, SDS),并使用SDS作为内部默认字符串类型(下文提到Redis动态字符串都用SDS表示),同时Redis也会在对字符串无需修改的地方使用C字符串,例如:打日志。

定义

图中展示是用SDS的结构体,包含了三个属性值:SDS长度、未使用字节数量、字节数组。
SDS数据结构

  • free 属性记录buf数组未使用字节数量,0代表SDS没有分配任何未使用的字节数量。
  • len属性值buf数组中已使用的字节数量,5代表SDS表示当前保存字符串的长度
  • buf[] 属性是用于保存字符串数组,char类型数组。
    图中我们可以看出,SDS遵循C字符串以“空串”结尾的惯例,目的是为了可以重用C字符串库中的函数,例如打印操作:prinf(s->buf);

特性

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

  • 相比较C字符串,SDS会提供一个属性值len来记录字符串长度,不需要从头到尾遍历一遍对字符进行计数,因此时间复杂度可以从O(n)降到O(1)。

杜绝缓冲区溢出

  • 因为C字符串不记录自身长度,导致C中strcat函数在做字符串拼接的时候,原字符串没有足够的内存导致内存溢出;当SDS API对SDS进行修改时,会先检查当前空间是否满足需求,如果不满足会先扩展SDS空间,然后再执行拼接操作。

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

  • 空间预分配: SDS会在修改字符串同时不仅会分配必须的空间,还会分配相同大小额外未使用的空间。如果对SDS修改后,SDS的大小小于1M,这时SDS的free属性将和len属性保持一致。如果修改后SDS的大小大于1M,此时free属性将保持1M不会变化。
    例如:修改之后SDS大小变为13个字节,此时len属性大小为13,free属性大小为13,此时buf数组的实际长度为13+13+1 = 27

  • 惰性空间释放:当SDS需要缩短字符串时,并不会立即回收缩短后的内存,而是使用free属性将这些记录起来,并供将来使用。

兼容部分C串函数

  • 为了可以尽可能多重用C库中操作字符串的函数,SDS遵循了C字符串以空串结尾的惯例,这样Redis就不需要单独的实现操作SDS的函数了,避免了不必要的重复代码工作。

二进制安全

  • 因为SDS有记录长度的len属性,这可以使SDS不需要使用空串来判断当前是否结束。SDS里面保存数据的二进制格式,程序不会对数据进行限制或者过滤,因此是二进制安全的数据类型。
    相比较C字符串,SDS可以保存除了文本以外数据,例如:图片、音频、视频、压缩文件。

总结

下面是针对C字符串和SDS的区别做了一些总结:

C字符串 SDS
获取字符串长度的时间复杂度为O(n) 获取字符串长度的时间复杂度为O(1)
API不安全,可能造成缓存溢出 API安全,不会造成溢出
修改字符串N次必然需要内存重分配N次 不一定需要N次,最坏情况下为N次
只能保存文本数据 可以保存文本和任意二级制数据
占用空间小 占用空间大

参考资料

《Redis设计与实现》第二章 简单动态字符串

能力有限,如果文中存在不妥当的内容,恳请及时指正!

猜你喜欢

转载自blog.csdn.net/weixin_42677165/article/details/89957058
今日推荐