Redis学习历程(一)

字符串

Redis里使用的字符串又叫做简易动态字符串,其实这里它这里的设计与 C++ 的 string 类有异曲同工之妙。

SDS

先看看 SDS 的定义。

struct sdshdr {
    // 记录 buf 数组中已使用字节的数量
    // 等于 SDS 所保存字符串的长度
    int len;

    // 记录 buf 数组中未使用字节的数量
    int free;

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

这里要说的是,SDS 遵循 C 字符串的定义模式,末尾以空字符结尾,保存空字符的 1 字节长度并不计算在 len 属性里。

扩容与回收

当进行一次对 SDS 的操作之后,SDS 变长之后的SDS的长度,即 len 的属性值小于 1 MB时,程序将分配大小等于 len 的长度的额外空间给 SDS,此时 free 的长度等于 len。这时候 buf 的实际长度为 len + free + 1。

如果操作之后变长的 SDS 长度大于 1 MB,程序则分配大小为 1 MB 的额外空间给 SDS。比如,修改后的 len 长度为 20 MB,那么此刻分配给 free 长度 1 MB,此时的 buf 数组长度为 20 MB + 1 MB + 1 byte。

如果对 SDS 操作后,SDS 变短,此刻不会立即回收 buf 数组的长度,而是将空余长度保存到 free 属性里,留给后面可能的使用。

通过空间预分配的策略,可以减少连续操作字符串后,导致空间重分配的次数,进而提升速率,本质上是空间换时间的方案。

二进制安全

为了确保 Redis 可以适用于不同的场景, SDS 的 API 都是二进制安全的,程序对 SDS 处理都是以处理二进制的方式处理存在 buf 数组里的数据,输入写入什么样读出来就什么样,它使用 len 来判断字符串是否读到了结束,这样避免了 C 里以空字符为结束,导致某些字符串截断的问题。

与 C 字符串相比

获取字符串长度的时间复杂度更低,达到了O(1),SDS 的 API 都是安全的,不会造成缓冲器溢出等问题。修改字符串 N 次,最多执行 N 次空间重分配。兼容二进制数据。


链表

定义

链表定义如下:

typedef struct list {
    // 表头节点
    listNode *head;

    // 表尾节点
    listNode *tail;

    // 链表所包含的节点数量
    unsigned long len;

    // 节点值复制函数
    void *(*dup)(void *ptr);

    // 节点值释放函数
    void (*free)(void *ptr);

    // 节点值对比函数
    int (*match)(void *ptr, void *key);
} list;

链表节点定义如下:

typedef struct listNode {
    // 前置节点
    struct listNode *prev;

    // 后置节点
    struct listNode *next;

    // 节点的值
    void *value;
} listNode;

可以看到,本质上 reids 里的链表是一个双端链表,并且结构内维护了链表的长度,以及对链表内节点进行复制、释放、比较的函数指针。

你可能感兴趣的:(Redis学习记录)