Redis2.8和4.0的基本数据结构

redis数据结构,网上资料也很多,redis设计与实现书上讲的也很明白,具体的也可以参考博客专栏,这里只对redis2.8和4.0版本做个简单的对比:
http://blog.csdn.net/column/details/15428.html

(sds,list,dictht,skiplist,intset,ziplist,quicklist)
简单动态字符串(sds),在Redis需要的不止是字符串字面量,而是一个可以被修改的字符串值时,Redis会使用SDS来表示,这里可以先看其定义,在2.8和4.0版本中定义是不一样的,4.0中的版本中有sdshdr5、sdshdr8、sdshdr16、sdshdr32、sdshdr64五种类型,可以在sds.h文件中找到。

redis2.8
struct sdshdr{
int len;// 记录buf数组中已经使用的字节数量
int free//记录buf中未使用的字节数量
char buf[];//字节数组,用于保存字符串
};
redis4.0
struct __attribute__ ((__packed__)) sdshdr64 {
    uint64_t len; //记录buf数组中已经使用的字节数量
    uint64_t alloc; //分配的总字节数
    unsigned char flags; //用0、1、2、3、4代表类型
    char buf[];
};

SDS有以下好处:
1、可以以O(1)获取数组长度。
2、避免缓冲区溢出,因为当SDS需要修改时,会先检查空间是否足够,不够的话会自动扩容,然后修改。
3、减少内存重分配,c中对append或者trim操作会重新分配内存,而sds通过空间预分配和惰性空间释放对其进行了优化。
(空间预分配是在修改SDS之后,对于SDS的长度小于1MB时,程序会分配和当前数组len长度一致的free空间,而当修改的SDS的总长度大于1MB,则会多分配1MB的free空间。惰性空间释放既在对SDS进行截断或者缩短时,不会立即回收多出来的内存,而是将free增加,这样在可以保证避免频繁操作,当日SDS也提供了API可以真正释放内存。)

链表(list )是列表键的底层存储结构之一,定义在adlist.h文件中,链表类型在2.8中用于list的实现,在4.0中list则全部使用quicklist实现。
redis2.8和4.0中一致

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

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;

字典(dictht)是哈希表的底层实现,一个哈希表里面有两个哈希表节点,一个用于存储,一个用于迁移数据,同样2.8和4.0的结构一致,可在dict.h文件中找到。

typedef struct dictEntry {
    void *key;//键
    union {//值
        void *val;
        uint64_t u64;
        int64_t s64;
        double d;
    } v;
    struct dictEntry *next;//下一个哈希表节点,链表形式
} dictEntry;

/* This is our hash table structure. Every dictionary has two of this as we
 * implement incremental rehashing, for the old to the new table. */
typedef struct dictht {
    dictEntry **table;//哈希表数组
    unsigned long size;//哈希表大小
    unsigned long sizemask;//哈希表大小掩码,用于计算索引值
    unsigned long used;//哈希表已有节点数量
} dictht;

跳跃表(skiplist),跳跃表可在redis.h文件中找到,其中zkiplistNode和zskiplist为其定义,2.8和4.0定义一致。

typedef struct zskiplistNode {
    robj *obj;//节点成员对象
    double score;//节点分值,从小到大排序
    struct zskiplistNode *backward;//后退指针,指向当前节点的前一个节点
    struct zskiplistLevel {
        struct zskiplistNode *forward;//前进指针,用于访问表尾方向的其他节点
        unsigned int span;//跨度,记录前进指针所指节点和当前节点距离
    } level[];
} zskiplistNode;

typedef struct zskiplist {
    struct zskiplistNode *header, *tail;//表头尾节点
    unsigned long length;//跳跃表长度,也就是当前包含节点长度
    int level;//当前跳跃表层数最大的节点层数
} zskiplist;

typedef struct zset {
    dict *dict;
    zskiplist *zsl;
} zset;

整数集合(intset)是集合键的底层实现之一,当一个集合只包含整数元素,并且这个集合元素数量不多时,Redis就会使用整数集合作为集合键的底层实现,其中2.8和4.0定义一致。

typedef struct intset {
    uint32_t encoding;//编码方式
    uint32_t length;//集合包含的元素数量
    int8_t contents[];//保存元素的数组
} intset;

压缩列表(ziplist)是列表键和哈希键的实现之一,在2.8中当一个列表键只包含少量列表时会使用ziplist,在4.0中列表键则是quicklist的实现,其中节点结构可在ziplist.c文件中找到,2.8和4.0的定义一致。

typedef struct zlentry {
    unsigned int prevrawlensize; //编码 前驱节点的长度所需字节大小
    unsigned int prevrawlen;     //前驱节点长度
    unsigned int lensize;        //编码当前节点长度所需字节大小
    unsigned int len;            //当前节点值长度
    unsigned int headersize;     //prevrawlensize + lensize.
    unsigned char encoding;      // Set to ZIP_STR_* or ZIP_INT_* 当前节点编码格式
    unsigned char *p;            //指向当前节点指针
} zlentry;

(quicklist)是一个有ziplist组成的双向链表,从3.2版本新增的,可以在quicklist.h中找到其定义。

typedef struct quicklistNode {
    struct quicklistNode *prev;//前驱节点指针
    struct quicklistNode *next;//后驱节点指针
    unsigned char *zl;//不设置压缩数据参数时指向ziplist,设置时指向quicklistLZF
    unsigned int sz;           //压缩列表ziplist总长度
    unsigned int count : 16;    //ziplist中包的节点数,占16bists
    unsigned int encoding : 2;   /* RAW==1 or LZF==2 */是否采用LZF压缩算法压缩quicklist节点
    unsigned int container : 2;  /* NONE==1 or ZIPLIST==2 */表示一个quicklistNode是否采用ziplist,1表示没压缩,2表示压缩了
    unsigned int recompress : 1; //标记quicklist节点的ziplist是否被压缩过,占1bit
    unsigned int attempted_compress : 1; //测试时使用
    unsigned int extra : 10; //额外扩展位
} quicklistNode;

typedef struct quicklist {
    quicklistNode *head;//指向头节点指针
    quicklistNode *tail;//指向尾节点指针
    unsigned long count;  //ziplist节点计数器
    unsigned int len;           //quicklistNode节点计数器
    int fill : 16;              //ziplist的大小,配置文件设置,16bits
    unsigned int compress : 16; //压缩程度值,配置文件设置,16bits,0表示不压缩
} quicklist;

不同类型和编码对象对应关系,以及转换条件
- String:int(使用整数值实现的字符串对象,如果一个字符串对象保存的是整数值,并且可以用long表示),embstr(使用embstr编码的简单动态字符串实现的字符串对象,字符串对象保存的是字符串值,并且字符串值长度小于39字节,),raw(使用简单动态字符串实现的字符串对象,字符串对象保存的是字符串值,并且字符串值长度大于39字节)
- List:ziplist,linkedlist,当列表对象保存的所有字符串长度都小于64字节,且保存的元素数量小于512个时用ziplist。
- Hash:ziplist,hashtable,哈希对象保存的键值对的键和值字符串长度都小于64字节,且保存的键值对数量小于512使用ziplist。
- Set:intset,hashtable,集合对象保存的元素都是整数值,且集合对象保存的元素数量不超过512个时用intset。
- Zset:ziplist,skiplist和Hashtable,有血集合保存的元素小于128个,且有序集合保存的所有元素成员的长度都小于64字节时使用ziplist。注意在因为类似ZRANK和ZRANGE命令用skiplist,ZSCORE则从Hashtable获取。

你可能感兴趣的:(redis)