Redis 概述

Redis(remote dictionary server)远程字典服务,用C语言编写,支持网络,可基于内存也可以持久化的日志型,key-value数据库。提供了多种语言的api,性能高、速度快。

Redis官网

数据结构与对象

redis 的 key 和 value 都是对象,其中key是字符串对象,而value则可以是多种对象,共以下5种:

string list hash set sorted set
字符串 列表 哈希 集合 有序集合

简单动态字符串(SDS)

redis 存在一种对象类型叫做:simple dynamic string 动态字符串,即SDS。

基于C语言的结构体,SDS包含一个字节数组和两个默认属性,这两个属性分别记录了,所保存字符串的长度len,以及数组空闲的空间free。

struct sdsshdr {
    int len;		// 记录字符串长度信息
    int free;		// 记录未使用的空间长度
    char buf[];		// 保存二进制字节
}

基于这种特性,下面来谈谈redis的sds结构相比较于C语言常规字符串的优势。

  1. 获取长度效率高

    • 由于C语言字符串并不记录自身的长度信息,想要获取其长度时,需要遍历整个字符串,时间复杂度为O(n)。

    • 而sds 由于有len这一信息保存了长度,因此获取长度的时间复杂度为:O(1)。

  2. 杜绝缓冲区溢出

    • 在C语言中,若要拼接两个字符串s1、s2,则必须要提前给s1分配足够的内存,否则会发生溢出。

    • 而SDS在修改字符串时,首先会检索空间是否足够,若不足够,则动态扩容。

  3. 减少内存重分配次数

    • C语言中修改字符串需要做大量的内存重新分配,如在扩容时需要重新分配内存,缩短时需要回收内存,这样的话在遇到高频率的字符串修改时,性能会有所下降。

    • redis的sds基于自身的free和len属性,针对于扩容和缩短字符串两种不同的应用场景,实现了两种优化策略。

      • 空间预分配策略

        当扩容字符串后:

        1. 若len < 1MB,则给sds再分配len的空间,即:

          分配前:len,free = 0;

          分配后:len, free = len

        2. 若len >= 1MB,则给sds分配1MB的空间,即:

          分配前:len,free = 0;

          分配后:len,free = 1MB

      • 惰性空间释放

        当缩短字符串时,sds并不急于回收内存,而是用free来记录。

        与此同时,sds也提供了API可以在需要时回收这些未使用空间,以避免空间资源浪费。

  4. 二进制安全

    • C字符串在遇到空格时会认为是字符串结尾,使得C字符串无法保存二进制信息。
    • sds的buf数组用于保存二进制字节
  5. 兼容部分C的字符串函数
    Redis兼容了部分C函数,避免代码重复。

参考资料:
《redis设计与实现》——黄健宏​