多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
`Redis`是基于`C`语言进行开发的,而`C`语言中的字符串是二进制不安全的,所以`Redis`就没有直接使用`C`语言的字符串,而是自己编写了一个新的数据结构来表示字符串,这种数据结构称之为简单动态字符串(Simple dynamic string),简称`sds`。 在`C`语言中,字符串采用的是一个`char`数组(柔性数组)来存储字符串,而且字符串必须要以一个空字符串`\0`来结尾。字符串并不记录长度,所以如果想要获取一个字符串的长度就必须遍历整个字符串,直到遇到第一个`\0`为止(`\0`不会计入字符串长度),故而获取字符串长度的时间复杂度为`O(n)`。 正因为`C`语言中是以遇到的第一个空字符`\0`来识别是否到了字符串末尾,因此其只能保存文本数据,不能保存图片、音频、视频和压缩文件等二进制数据,否则可能出现字符串不完整的问题,所以其是二进制不安全的。 `Redis`中为了实现二进制安全的字符串,对原有`C`语言中的字符串实现做了改进。如下所示就是一个旧版本的`sds`字符串的结构定义: ~~~c struct sdshdr{ int len;//记录buf数组已使用的长度,即SDS的长度(不包含末尾的'\0') int free;//记录buf数组中未使用的长度 char buf[];//字节数组,用来保存字符串 } ~~~ 经过改进之后,如果想要获取`sds`的长度不用去遍历`buf`数组了,直接读取`len`属性就可以得到长度,时间复杂度一下就变成了`O(1)`,而且因为判断字符串长度不再依赖空字符`\0`,所以其能存储图片、音频、视频和压缩文件等二进制数据,不用担心读取到的字符串不完整。 需要注意的是,`sds`依然遵循了`C`语言字符串以`\0`结尾的惯例,这么做是为了方便复用`C`语言字符串原生的一些 API,换言之就是在`C`语言中会以碰到的第一个`\0`字符作为当前字符串对象的结尾,所以如果一些二进制数据就可能会出现读取字符串不完整的现象,而`sds`会以长度来判断是否到字符串末尾。 在`Redis 3.2`之后的版本,`Redis`对`sds`又做了优化,按照存储空间的大小拆分成为了`sdshdr5`、`sdshdr8`、`sdshdr16`、`sdshdr32`、`sdshdr64`,分别用来存储大小为:`32`字节(`2`的`5`次方),`256`字节(`2`的`8`次方),`64KB`(`2`的`16`次方),`4GB`大小(`2`的`32`次方)以及`2`的`64`次方大小的字符串(因为目前版本`key`和`value`都限制了最大`512MB`,所以`sdshdr64`暂时并未使用到)。`sdshdr5`只被应用在了`Redis`的`key`中,`value`中不会被使用到,因为`sdshdr5`和其它类型也不一样,其并没有存储未使用空间,所以比较适用于使用大小固定的场景(比如`key`值): ![](https://img.kancloud.cn/76/2d/762d1e1c0edb60fc821aafbe8cfd0e53_757x491.png) 任意选择其中一种数据类型,其字段代表含义如下: ~~~c struct __attribute__ ((__packed__)) sdshdr8 { uint8_t len; //已使用空间大小 uint8_t alloc; //总共申请的空间大小(包括未使用的) unsigned char flags; //用来表示当前sds类型是sdshdr8还是sdshdr16等 char buf[]; //真实存储字符串的字节数组 }; ~~~ 可以看到相比较于`Redis 3.2`版本之前的`sds`,主要是修改了`free`属性然后新增了一个`flags`标记来区分当前的`sds`类型。