Redis基础知识(学习笔记11--SDS)

1.SDS介绍(为什么要提出SDS)

无论是Redis的key还是Value,其基础数据类型都是字符串。例如,Hash型Value的field 与 Value的类型、List型、Set型、ZSet型Value的元素都是字符串。虽然Redis是使用标准C语言开发的,但并没有直接使用C语言中传统的字符串表示,而是自定义了一种字符串。这种字符串本身的结构比较简单,但功能却非常强大,称为简单动态字符串,Simple Dynamic String,简称SDS。

注意:Redis中的所有字符串并不都是SDS,也会出现C字符串。C字符串只会出现在字符串“字面常量”中,并且该字符串不可能发生变更。例如,Redis 返回的结果中包含的字符串就是C语言的字符串。

2.SDS结构

SDS 不同于C字符串。C字符串本身是一个以双引号括起来,以空字符串‘\0’结尾的字符串序列。但SDS是一个结构体,定义在Redis安装目录下的 src/sds.h中。

struct sdshdr {
//字节数组,用于保存字符串
char buf[];

//buf[]中已使用字节数量,称为SDS的长度
int len;

//buf[]中尚未使用的字节数量
int free;

}

  例如执行 set countey "China" 命令时,键country 与值 ”China“ 都是SDS类型的,只不过一个是SDS的变量,一个是SDS的字面常量,”China“在内存中的结构如下:

 通过以上结构可以看出来,SDS的buf值实际上是一个C字符串,包含空字符串‘\0’共占6个字节。但SDS的len是不包含‘\0’的。

在实际的使用过程中,经常会做一些预分配,变成下面这种结构。

 该结构与前面不同的是,这里面有3字节未使用空间。

补充

type keyname

可以查看那种类型。

object encoding keyname

可以查看key对应的value在内存中的存储类型。例如,如果返回的是"embstr",就是我们前面说的SDS。

3.SDS的优势

C字符串使用len+1长度的字符串数组来表示实际长度为len的字符串,字符串数组最后以空字符串’\0‘结尾,表示字符串结尾。这种结构简单,但不能满足Redis对字符串功能性、安全性及高效性等的要求。

(1)防止“字符串长度获取”的性能瓶颈

对于C字符串,若要获取其长度,则必须要通过遍历整个字符串才能获取得到。对于超长字符串的遍历,会成为系统的性能瓶颈。而由于SDS结构体中直接就存放着字符串的长度数据,所以对于获取字符串长度需要消耗的系统性能,特别是在长字符串的情况下,不会成为Redis的性能瓶颈。

(2)保障二进制的安全

C字符串中只能包含符合某种编码格式的字符,例如ASCII、UTF-8等,并且除了字符串尾外,其他位置是不能包含空字符串’\0‘的,否则该字符串就会被程序误解为提前结束,而在图片、音频、视频、压缩文件、office 文件等二进制数据中常以空字符串'\0'作为分隔符的情况是很常见的,故而在C字符串中是不能保存图片、音频、视频、压缩文件、office 文件等二进制数据的。

但SDS不是以空字符串'\0'作为结束标志的,其是通过len属性来判断字符串是否结束的。所以,对于程序处理SDS中的字符串数据,无需对数据做任何限制、过滤、假设,只需读取即可。数据写入的是什么,读到的就是什么。

(3)减少内存再分配次数

SDS采用了空间预分配策略惰性空间释放策略来避免内存再分配问题。

空间预分配策略是指,每次SDS进行空间扩展时,程序不但为其分配所需的空间,还会为其分配额外的未使用空间,以减少内存再分配次数。而额外分配的未使用空间大小取决于空间扩展后SDS的len属性值。

***如果len属性值小于1M,那么分配的未使用空间free的大小与len相同。

***如果len的属性值大于等于1M,那么分配的未使用空间free的大小就是1M。

SDS对于空间释放采用的时惰性空间释放策略。该策略是指,SDS字符串长度如果缩短,那么多出的未使用空间将暂时不释放,而是增加到free中。所以后期扩展SDS时减少内存再次分配。

如果要释放SDS的未使用空间,则可以通过sdsRemoveFreeSpace()函数来释放。

(4)兼容C函数

Redis中提供了很多SDS的API,以方便用户对SDS进行二次开发。为了能够兼容C函数,SDS的底层数组buf[]中的字符串仍以空字符串'\0'结尾。

4 常用的SDS操作函数

函数 功能描述
sdsnew() 使用指定的C字符串创建一个SDS
sdsempty(); 创建一个不包含任何字符串数据的SDS
sdsdup(const sds s);  复制字符串(创建一个指定SDS的副本)
sdsfree() 释放指定的SDS
sdsclear() 清空指定SDS的字符串内容
sdslen() 获取指定SDS的已使用空间len值
sdssaveall() 获取指定SDS的未使用空间free值
sdsMakeRoomFor() 使指定的SDS的free空间增加指定的大小
sdsRemoveFreeSpace() 释放指定SDS的free空间
sdscat() 将指定的C字符串拼接到指定SDS字符串末尾
sdscatsds() 将指定的SDS的字符串拼接到另一个指定的SDS字符串末尾
sdscpy() 将指定的C字符串赋值到指定的SDS中,覆盖原SDS字符串内容
sdsgrowzero(sds s, size_t len); 扩展SDS字符串到指定长度,这个扩展是使用空字符串’\0‘填充
sdsrange(sds s, int start, int end) 截取指定SDS中指定范围内的字符串
sdstrim(sds s, const char *cset);   在指定SDS中删除所有只当C字符串中出现的所有字符
sdsemp() 对比两个给定SDS字符串是否相同
sdstolow() 将指定SDS字符串中的所有字面变为小写
sdstouper() 将指定SDS字符串中的所有字面变为大写

学习参阅声明

【Redis视频从入门到高级】

https://www.bilibili.com/video/BV1U24y1y7jF?p=11&vd_source=0e347fbc6c2b049143afaa5a15abfc1c】

posted @ 2024-06-30 14:29  东山絮柳仔  阅读(9)  评论(0编辑  收藏  举报