Redis | 简单动态字符串(SDS)

简单动态字符串

? Redis 没有直接使用C语言的字符串,而是自己构建了一个名为简单动态字符串的扫象类(SDS),并将SDS做为Redis的默认字符串。

? C语言的字符串,在Redis中,只会做一些日志处理的工作,因为这些工作不需要对字符串进行修改。而需要修改的字符串的都会采用 SDS。

比如下面这个例子:

    set msg "hello,world"

? 这个命令是一个简单的存储命令,键是 msg 值是 hello,world,这个动作会在数据库中创建一个新的键值对。

那么这个键值对的键和值都是由 SDS 实现的。

SDS 除了用来保存数据库的对象以外,还被用做数据库的缓冲区的实现。

SDS 结构的定义

  struct sdshdr{
    // 数组中已保存的字符长度
    int len;
    // 数组中还未使用的长度
    int free;
    // 字切数组,用于保存字符串
    char buf[];
  }

? 在buf数组中,最后一位永远都是/0 ,这一位也不会记入数组的长度中,对我们使用者来说,可以完全当它不存在,但是这个/n 存在的好处却是非常大的,因为C语言的字符串本来未尾就自带了一个 /0 ,Redis 的SDS 后面也加一个数组 ,可以使Redis 完全可以重用C语言字符串的API。

SDS 和 C语言原生字符串的比效

1、获取字符串长度

? 原生字符串获取长度的时间复杂度为 O(n) , 因为它没有记录自身长度的字段,所以如果想要获取字符串的长度,就必须要遍历整个字符串,直到遇到 /0 为止,才会返回,而SDS的 len 属性中本来就维护了一个自身长度的属性,所以获取 SDS 长度的时间复杂度为 o(1)。

? 并且 SDS 的统计字符串的长度工作是在执行APi的时候 ,自动执行的,并且不需要我们手动设置,可以方便我们的使用,所以这保证了获取字符串长度的工作不会使Redis 陷入性能瓶颈,就算一直对一个字符串一直使用 STRLEN 命令,也不会造成任何影响 ,因为Redis 获取字符串长度的命令,时间复杂度仅为 O(1)。

2、保证缓存区不会益出

? 使用C语言原生字符串的害处就是,如果修改一个字符串的时候,忘让给他重新分配空间,可能导致缓冲区益出,并且会覆盖别的数据。

? 在使用SDS的时候 ,则不会出现这个情况,因为每一次修改SDS字符串的时候 ,都会先检查free属性,查看空间是否足够。如果不够的话,则会先对SDS进行扩容,然后才是对字符串进行修改。

? 如果是在别的应用中,如果每一次修改字符串时去扩容或者缩容,是完全可以理解的,但是Redis是一个数据库,无法忍受这样性能浪费,所以每一次扩容的时候,都会为下一次修改预留空间,分配的规则如下:

如果SDS 的长度小于 1M,那么每一次增加的空间长度为此时len的长度,增加后 free 和 len 的长度应该是一样的。此时的长度计算为: free + len + 1

如果SDS 的长度大于 1M ,那么每一次增加 1M 的空闲空间,此时SDS的长度为:len + 1M + 1

? 因为通过空间预分配,所以Redis 可以减少连续执行字符串增长操作所需要的内存重新分配次数,并且这些分配的空间,并不会因为字符串的缩短而被回收,而是会留在SDS中,以防止未来会有这一方面的需要。并且我们也不需要担心这些空间会对内存造成什么影响,SDS 的APi会在真正有用的时候 ,对这些空间进行释放。

二进制安全

? 我们在使用Redis 的时候 ,可以直接保存图片,视频,音乐。。。那是因为Redis存在buf里面的数据是二进制的,并不是简单的字符串,也不会对二进制数据进行处理,过滤等操作,这也保证了,我们存入数据的时候是什么样,读取数据的时候就是什么样。

? 而C语言就不可以这样了,因为C语言会默认以 /0 字符进行分割字符串,所以便得C字符串除了结尾包含空字符外,中间的字符都不能包含这样的字符,这就极大的限制了C原生字符串的使用范围。



细节决定成败!
个人愚见,如有不对,恳请扶正!

Redis | 简单动态字符串(SDS)

上一篇:接口测试DAY1


下一篇:0 1背包问题之leetcode总结