基础知识

Redis源码研究之底层数据结构

2018-04-25  本文已影响23人  wenmingxing

本文主要介绍Redis的几种底层数据结构的数据结构部分,其接口部分以后会分别说明。

建议阅读:
1、Redis的几种数据结构的理论说明见:Redis中的对象底层实现

I、dict

字典是数据库最基本的数据类型,因为数据库几乎都是以key-value形式存储的。

// 字典数据结构,Redis 的所有键值对都会存储在这里。其中包含两个哈希表,为了应对rehash。
/*src/dict.h/dict*/
typedef struct dict {
    // 哈希表的类型,包括哈希函数,比较函数,键值的内存释放函数
    dictType *type;
    // 存储一些额外的数据
    void *privdata;
    // 两个哈希表,ht[1]用于完成rehash
    dictht ht[2];
    // 哈希表重置下标,指定的是哈希数组的数组下标
    int rehashidx; /* rehashing not in progress if rehashidx == -1 */
    // 绑定到哈希表的迭代器个数
    int iterators; /* number of iterators currently running */
} dict;

II、redisObject

在Redis的数据库存储结构中,每个value都会被包装为一个redisObject(其中包含了指定value的类型,编码方式等属性):

/*src/redis.h/redisObject*/
typedef struct redisObject {
    // 刚刚好32 bits
    // 对象的类型,字符串/列表/集合/哈希表
    unsigned type:4;

    // 编码的方式,Redis 为了节省空间,提供多种方式来保存一个数据
    // 譬如:“123456789” 会被存储为整数123456789
    unsigned encoding:4;

    // 当内存紧张,淘汰数据的时候用到
    unsigned lru:22; /* lru time (relative to server.lruclock) */

    // 引用计数,用于整型字符串,为节省空间实现了共享内存
    int refcount;

    // 数据指针,指向实际的value
    void *ptr;
} robj;  

III、zset

zset为有序集合,主要为一个skiplist+dict,其是一种AVL树的替代品,实现简单:

/*src/redis.h/zset*/
typedef struct zset {
    // 哈希表
    dict *dict;  //为了在O(1)时间查找
    // 跳表
    zskiplist *zsl;    //为了有序,并顺序查找
} zset;  
/* src/redis.h/zskiplist */
typedef struct zskiplist {

    // 表头节点和表尾节点
    struct zskiplistNode *header, *tail;

    // 表中节点的数量
    unsigned long length;

    // 表中层数最大的节点的层数
    int level;

} zskiplist;
/* ZSETs use a specialized version of Skiplists */
/* src/redis.h/zskiplistNode */
typedef struct zskiplistNode {

    // 成员对象
    robj *obj;

    // 分值
    double score;

    // 后退指针
    struct zskiplistNode *backward;

    // 层
    struct zskiplistLevel {

        // 前进指针
        struct zskiplistNode *forward;

        // 跨度
        unsigned int span;

    } level[];

} zskiplistNode;

IV、 adlist

一个双向链表结构:

/*src/adlist.h/list*/
 typedef struct list {
    // 头指针
    listNode *head;
    // 尾指针
    listNode *tail;
    // 数据拷贝函数指针
    void *(*dup)(void *ptr);
    // 析构函数指针
    void (*free)(void *ptr);
    // 数据比较指针
    int (*match)(void *ptr, void *key);
    // 链表长度
    unsigned long len;
} list;  

V、ziplist

为了优化内存而实现的压缩链表,其实是一个数组,这样能快速的导入到CPU cache中:

/* 保存 ziplist 节点信息的结构 */
/*src/ziplist.c/zlentry*/
typedef struct zlentry {

    // prevrawlen :前置节点的长度
    // prevrawlensize :编码 prevrawlen 所需的字节大小
    unsigned int prevrawlensize, prevrawlen;

    // len :当前节点值的长度
    // lensize :编码 len 所需的字节大小
    unsigned int lensize, len;

    // 当前节点 header 的大小
    // 等于 prevrawlensize + lensize
    unsigned int headersize;

    // 当前节点值所使用的编码类型
    unsigned char encoding;

    // 指向当前节点的指针
    unsigned char *p;

} zlentry;  

VI、intset

表示整数集合:

/*src/intset.h/intset*/
typedef struct intset {
    // 每个整数的类型
    uint32_t encoding;
    // intset 长度
    uint32_t length;
    // 整数数组
    int8_t contents[];
} intset;  

VII、sds

二进制安全的动态字符串:

/*src/sds.h/sds*/  
typedef char *sds;

【参考】
[1] 《Redis设计与实现》
[2] 《Redis源码日志》

上一篇下一篇

猜你喜欢

热点阅读