本文参考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 123sdshdr的值如下

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进行追加字符操作,就会浪费一部分的内存空间,而且这部分的内存空间不会被主动释放。