本文参考redis系列丛书《Redis设计与实现》
redis字符串介绍
redis并没用我们常用的C语言中的字符串进行表示,而是自己创建了一个叫 简单动态字符串(简称SDS全称Simple Dynamic String) 的抽象类型。
内部构造如下
//****redis3.2之前的版本************************************
struct sdshdr {
// 记录 buf 数组中已使用字节的数量
// 等于 SDS 所保存字符串的长度
int len;
// 记录 buf 数组中未使用字节的数量
int free;
// 字节数组,用于保存字符串 这里char的实际长度为 len+1 最后一个字符串用来保存 '\0'
char buf[];
};
//****redis3.2之后的版本************************************
struct sdshdr {
//不同类型的SDS仅uint8_t类型不同例如uint16_t、uint32_t等
// 等于 SDS 所保存字符串的长度
uint8_t len;
//除去末尾\0外,申请了多少空间用于存储字符串
uint8_t alloc;
//前三位表示当前这个SDS的类型,后五位为保留值方便后续使用
unsigned char flags;
//字节数组,用于保存字符串 这里char的实际长度为 len+1 最后一个字符串用来保存 '\0'
char buf[];
};
为了方便理解,本文将会按照redis3.2版本之前的SDS结构进行讲解
SDS是二进制安全的
在c字符串中,字符串判定到末尾的条件是‘\0’ ,这样我们存储图片、文件等信息后,在读取的时候,就不能被正确的读取出来,因为它读到第一个‘\0’就停止了,而SDS使用了len用来记录字符串的真正长度,并且判定字符串结束的标识就是len,在读取的时候,可以正确读出所有字符除串,所以我们就可以用SDS进行存储文件。因为它是二进制安全的。
redis的字符串追加操作流程
1、当我们执行set wxz wang
时,sdshdr的值如下
len = 4;
free = 0;
buf = "wang\0";//buf当前所占字节为len + free + 1 = 5
需要注意的是,这时的free为空,也就是说,当前这个字符串被占满了,如果需要对这个字符串进行追加的时候,redis会对其额外创建空间。
2、接着我们执行APPEND
命令append wxz xingzhen
,sdshdr的值如下
len = 12;
free = 12;
buf = "wangxingzhen\0 ";//buf当前所占字节为len + free + 1 = 25
这时,我们发现len的值变成了字符串的长度12,free也变成12,这是因为在进行APPEND
命令时,redis为这个字符串多申请了当前大小*2的空间,这样的话,在我们在进行APPEND
命令时,如果追加的长度不超过free的大小,就不需要对buf重新分配。 ** '\0’后面的空为未使用的空间,也就是预分配空间 **。
例如我们执行append wxz 123
sdshdr的值如下
len = 15;
free = 9;
buf = "wangxingzhen123\0 ";//buf当前所占字节为len + free + 1 = 25
最后需要注意一下,在进行分配空间时,如果字符串长度小于1MB,那么预分配空间free等于字符串的长度len,如果字符串的长度大于1MB时,预分配空间是1MB。 (计算机内存很宝贵)
总结
SDS和c语言字符串相比
优点:
1、SDS记录了字符串的长度,这样在每次进行长度计算的时候,可以直接获取,而不是去重新计数。
2、在执行追加操作时,降低了内存的分配次数,这样就加快了操作速度。
3、二进制安全
缺点:
1、如果对redis进行追加字符操作,就会浪费一部分的内存空间,而且这部分的内存空间不会被主动释放。