通常,我们说Redis 是用 C 语言写的,但是对于Redis的字符串,却不是 C 语言中的字符串(即以空字符’\0’结尾的字符数组),它是自定义的数据结构SDS(simple dynamic string),并将 SDS 作为 Redis的默认字符串表示。

一、SDS 定义

struct sdshdr{
     //记录buf数组中已使用字节的数量
     //等于 SDS 保存字符串的长度
     int len;
     //记录 buf 数组中未使用字节的数量
     int free;
     //字节数组,用于保存字符串
     char buf[];
}

用SDS保存字符串 “Redis”具体图示如下:

解释: 

  1.  len 为字符串的实际长度  在redis中获取字符串的key长度的时间复杂度为O(1)
  2.  free 为 buf数组中剩余的空间大小(相对于 C 语言对于字符串的定义,多出了 len 属性以及 free 属性)
  3.  buf 保存字符串的数组
  4. 使用free杜绝了缓冲区溢出如果free的长度不够值的长度则自动会开辟len长度的空间

二、C字符串和SDS直接的区别

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

  由于 len 属性的存在,我们获取 SDS 字符串的长度只需要读取 len 属性,时间复杂度为 O(1)。而对于 C 语言,获取字符串的长度通常是经过遍历计数来实现的,时间复杂度为 O(n)。通过 strlen key 命令可以获取 key 的字符串长度。

②、杜绝缓冲区溢出

  我们知道在 C 语言中使用 strcat  函数来进行两个字符串的拼接,一旦没有分配足够长度的内存空间,就会造成缓冲区溢出。而对于 SDS 数据类型,在进行字符修改的时候,会首先根据记录的 len 属性检查内存空间是否满足需求,如果不满足,会进行相应的空间扩展,然后在进行修改操作,所以不会出现缓冲区溢出。

③、减少修改字符串的内存重新分配次数

  C语言由于不记录字符串的长度,所以如果要修改字符串,必须要重新分配内存(先释放再申请),因为如果没有重新分配,字符串长度增大时会造成内存缓冲区溢出,字符串长度减小时会造成内存泄露。

  而对于SDS,由于len属性和free属性的存在,对于修改字符串SDS实现了空间预分配和惰性空间释放两种策略:

  1、空间预分配:对字符串进行空间扩展的时候,扩展的内存比实际需要的多,这样可以减少连续执行字符串增长操作所需的内存重分配次数。

  2、惰性空间释放:对字符串进行缩短操作时,程序不立即使用内存重新分配来回收缩短后多余的字节,而是使用 free 属性将这些字节的数量记录下来,等待后续使用。(当然SDS也提供了相应的API,当我们有需要时,也可以手动释放这些未使用的空间。)

④、二进制安全

  因为C字符串以空字符作为字符串结束的标识,而对于一些二进制文件(如图片等),内容可能包括空字符串,因此C字符串无法正确存取;而所有 SDS 的API 都是以处理二进制的方式来处理 buf 里面的元素,并且 SDS 不是以空字符串来判断是否结束,而是以 len 属性表示的长度来判断字符串是否结束。

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

  虽然 SDS 是二进制安全的,但是一样遵从每个字符串都是以空字符串结尾的惯例,这样可以重用 C 语言库<string.h> 中的一部分函数。

三、总结

redis使用改数据结构的优点

  • 1:空间可以预分配
  • 2:惰性空间释放(使某个键的长度变小时内存不是立即回收而是增加free的大小)
  • 3:二进制安全(redis不是采用c语言字符串的以\0来判断字符串结束 而sds通过判断len的长度是否为0来判断字符串的长度)
  • 4:redis实际开辟的空间为len+free

注:一般来说,SDS 除了保存数据库中的字符串值以外,SDS 还可以作为缓冲区(buffer):包括 AOF 模块中的AOF缓冲区以及客户端状态中的输入缓冲区。

参考:https://www.cnblogs.com/ysocean/p/9080942.html

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐