redis

基本数据结构

直接看官方说明就行了:

基本的命令熟悉即可,大致知道各个数据类型有那些相应的操作,具体的可以翻手册: commands
关键是对于各个数据类型的具体应用场景心中要有大致的概念。

各个数据类型常见的应用场景

redis内部数据结构

SDS

redis使用自己封装的SDS作字符串表示(结构体里有free, len, buf字段,buf才是存数据用的), 相比c字符串,主要优势有:

struct sdshdr {
    // 记录buff数组中已使用字节的数量,等于SDS中保存字符串的长度
    int len;
    // 记录buf数组中未使用字节的数量
    int free;
    // 字节数组,用于保存字符串
    char buf[];
};

链表

lists用双向链表实现,主要有以下特点:

typedef struct listNode {
    // 前置节点
    struct listNode *prev;
    // 后置节点
    struct listNode *next;
    // 节点的值
    void *value;
}

typedef struct list {
    // 表头
    listNode *head;
    // 表尾
    listNode *tail;
    // 节点数量
    unsigned long len;
    // 节点值复制函数
    void *(*dup) (void *ptr);
    // 节点值释放函数
    void (*free) (void *ptr);
    // 节点值对比函数
    void (*match) (void *ptr, void *key);
}

字典

typedef struct dict {
    // 类型特定函数
    dictType *type;
    // 私有数据, 和上面的字段一起为了多态字典设置
    void *privdata;
    // 哈希表,一般只用ht[0],ht[1]在rehash的时候用
    dictht ht[2];
    // rehash索引,记录当前的进度,当rehash不在进行时,值尾-1
    int rehashidx;
}

typedef struct dictht {
    // 哈希表数组, 其中的每个元素是指向dictEntry的指针,每个dictEntry保存一个键值对
    dictEntry **table;
    // 哈希表大小
    unsigned long size;
    // 哈希表大小掩码,用于计算索引值,总是等于size-1
    unsigned logn sizemask;
    // 哈希表已有节点数量
    unsigned long used;
}


typedef struct dictEntry {
    // 键
    void *key;
    // 值
    union{
        void *val;
        uint64 u64;
        int64_t s64;
    } v;
    // 指向下个哈希表节点,行程链表
    struct dictEntry *next
}

redis哈希表使用链地址法(seperate chaining)来解决键冲突。因为哈希表数组中的每个元素都有next指针,可以构成一个单向链表。

哈希表根据负载因子(ht[0].used/ht[0].size)进行扩展或收缩,通过渐进式rehash来完成
(字典的rehashidx标记了上个完成的哈希数组的元素id,完成后加1下移,-1表示未有rehash行为)

跳跃表

sorted set底层实现之一。

typedef struct zskiplist {
    // 表头和表尾
    struct zskiplistNode *header, *tail;
    // 节点数量
    unsigned long length;
    // 层数最大的节点的层数
    int level;
} zskiplist;


typedef struct zskiplistNode {
    // 后退指针
    struct zskiplistNode *backward;
    // 分值,同一个跳跃表中可以相同。节点按分值大小排序,相同时按成员大小排序
    double score;
    // 成员对象,同个跳跃表中必须唯一
    robj *obj;
    // 层, 1-32的随机数
    sturct zskiplistLevel {
        // 前进指针
        struct zskiplistNode *forward;
        // 跨度
        unsigned int span;
    }
} zskiplistNode;

整数集合

typedef struct intset {
    // 编码方式
    uint32_t encoding;
    // 集合包含的元素数量
    uint32_t length;
    // 保存元素的数组,有序唯一。有需要的时候根据新加元素类型改变数组类型,即升级操作,
    // 好处是提升灵活性(不同类型整数放到同个集合中)和节约内存(有必要时才升级)
    int8_t constants[];
} intset;

对象

redis用对象来表示每个键值对的键和值, 会共享0到9999的字符串对象

typedef struct redisObject {
    // 类型
    usigned type:4;
    // 编码
    usigned encoding:4;
    // 指向底层实现数据结构的指针
    void *ptr;
    // 引用计数
    unsigned refcount;
    // 对象最后一次被命令程序访问的时间
    unsigned lru:22;
}

注:5.0的redis,list的底层实现只有quicklist了。

db

struct redisServer {
    ...
    // 数组,保存服务器中所有的数据库,用select命令选择连接的数据库;
    redisDb *db;
    // 服务器数据库数量;
    int dbnum;
    ...
}

typedef struct redisDb {
    ...
    // 数据库键空间,保存着数据库中所有的键值对,键是字符串对象,值是redis对象。
    dict *dict;
    // 过期字典,保存着键的过期时间,可以通过expire, pexpire, expireat, pexpireat设置, persist移除,ttl确认剩余时间。
    // redis通过惰性删除和定时删除来删除过期的键。
    dict *expires;
    ...

}

其他

参考资料