Redis 动态字符串 SDS 原理

发布于:2024-12-21 ⋅ 阅读:(11) ⋅ 点赞:(0)

SDS 原理

引言:

由于字符串是 redis 中使用最频繁的数据结构,例如 redis 中的 key,或者 value 也是多个字符串组成的。所以字符串在 redis 中是一个非常重要的数据结构。

问题:

但是 redis 中的字符串并不是使用 C 语言原先的字符串,这是因为 C 语言的字符串有很多问题。

char* s = "hello";
// 本质上是字符数组
// ['h', 'e', 'l', 'l', 'o', '\0']

由于 C 里面的字符串本质上是一个数组,并且是开辟的连续的内存空间需要有一个’\0’来作为结束标识。这就带来了以下问题:
● 不能直接获取到字符串长度,需要通过计算。
● 二进制不安全,不能内容中携带’\0’的内容
● 不能直接修改

详细说明:
首先第一条,由于C语言的数组并不能直接获取到长度,所以需要逐个去遍历直到遍历到结束标识才能够获取到数组的长度,这样就会导致时间复杂度是一个O(n),效率比较慢。
第二条,由于C语言中是靠结束标识来作为数组的结束符号的,那么如果我们存储的内容中包含了这个结束标识,就会导致字符串提前结束导致我们的内容不正确。
第三条,由于C语言想要扩容不能直接在原数组上修改,需要重新申请内存,然后把内容写到新分配的内存中,然后把旧的内存给释放掉,这样做十分消耗资源。

SDS结构说明

因为有了上面这些问题,所以redis就自己构建了一种新的字符串,叫做SDS(简单动态字符串)。
源码结构:
在这里插入图片描述

这个是C语言中的结构体,类似于Java中的类结构。

buf 数组:由于最后还是要保存字符串的数据的,所以 buf 数组就是用来存储最后的数据内容的。
len:就是用来保存这整个字符串的长度,是 redis 中 sds 用来维护的一个字段,通过这个字段就可以直接获取到字符串的长度,类型是一个 uint8_t 表示是一个无符号的 8 字节长度,能够表示的最大值就是 255,但是由于需要兼容C语言,buf数组最后还是会加一个结束标识,所以最后能保存的最大值是 254个字节。
alloc:这个是表示当前申请了多大的内存,由于C语言保存数据都需要申请内存,所以这个字段就是用来表示申请多大的内存的,这个字段和 len 不一样相等。
flags:用来表示不同的 sds 头大小,具体含义就是以上面这个源码为例,这个源码的len最多只能保存 254 个字节,但是如果超过了这个范围那么这个 sds 的结构就没有办法存储下来了,这时候就需要别的 sds 类型(或者说需要一个能够存储更大内容的sds类型)这时候这个 flags 就用来表示当前是哪种 sds 的数据类型。

有 flags 字段的原因就是因为在 redis 中 sds 有很多类型(每种类型能够保存的内容长度也不一样),所以需要这个字段进行区分(好处就是可以不过多的浪费资源,能够使用相近的内容去保存数据,不需要浪费过多的资源)。
具体查看源码:
在这里插入图片描述

可以看到里面有各种 sds 类型,可以表示不同的长度。

所以这里可以把前三个字段都可以看成是 sds 的头信息用来描述这个字符串的,最后一个数组其实是用来保存数据的。在这里插入图片描述

接下来以一个“name”进行举例:
在这里插入图片描述

这样就可以解决了上面C语言字符串中的一些问题了:
读取字符串长度:这里可以直接通过 len 字段读取长度非常快速。
二进制安全:由于sds维护了一个len的字段,这样在读取字符串的时候,只要从头开始读,读取len长度个字符就可以了,即使中间用’\0’也没有关系,因为统计了个数,说明这个结束标识其实是字符串内容,这样就可以保证二进制安全了。
动态扩容:
sds 是具有动态扩容的能力的,接下来举例说明一下:
一开始是"hi"这个sds:
在这里插入图片描述

如果这时候追加一个字符串",Amy",就需要申请新的内存空间
这里有两个原则:
● 如果新的字符串小于 1M, 那么新内存空间的长度为追加后的字符串长度的两倍+1
● 如果新的字符串大于 1M,那么新内存空间的长度为追加后的字符串长度+1M + 1
这种方式叫做内存预分配
这样做的原因是因为:其实申请内存空间这个操作非常消耗资源,如果频繁的申请空间会非常浪费资源,提前分配更多的空间可以减少申请内存的操作。

所以这里新申请的内存是 (2(hi) + 4(,amy)) * 2 + 1 = 13, 由于这个+1是用来为结束标识来申请的,所以实际 alloc 表示的就是 12。
在这里插入图片描述

总结sds的优点:

● 获取长度的时间复杂度为O(1)
● 二进制安全
● 动态扩容
● 减少内存分配次数


网站公告

今日签到

点亮在社区的每一天
去签到