redis底层数据结构简述

redis的数据库对象有五种,分别是字符串对象(key-value),列表对象(list),哈希对象(hash),集合对象(set),有序集合(sort set)。html

这些数据对象来自于底层数据类型的实现,这些数据类型分别为简单动态字符串,链表,字典,跳跃表,整数集合,压缩列表,对象java

如下是redisObject和底层数据结构的关系redis

简单动态字符串(SDS)

redis的键(KEY)由简单动态字符串实现,字符串对象(key-value)也是由简单动态字符串实现的。而简单动态字符串是由C实现的,它的结构以下算法

struct sdshr{
    //记录buf中已经使用的字节数量
    unsigned int len;
    //记录buf中未使用的字节数量
    unsigned int free;
    //字节数组,存储字符串
    char buf[];
}

在redis3.2版本后,SDS的结构发生了变化,有多种数据结构,根据状况使用数据库

//字符串长度小于32 Byte
struct __attribute__ ((__packed__)) sdshdr5 {
    unsigned char flags; /* 记录当前字节数组的属性,究竟是哪一个SDS结构 */
    char buf[];/* 保存字符串真正的值 */
};
//字符串长度小于256 Byte,大于32 Byte
struct __attribute__ ((__packed__)) sdshdr8 {
    uint8_t len; /* 记录当前数组的长度 */
    uint8_t alloc; /* 记录了当前字节数组总共分配的内存大小 */
    unsigned char flags; /* 记录当前字节数组的属性,究竟是哪一个SDS结构 */
    char buf[];/* 保存字符串真正的值 */
};
//字符串长度小于65536 Byte (64KB),大于256 Byte
struct __attribute__ ((__packed__)) sdshdr16 {
    uint16_t len; /* 记录当前数组的长度 */
    uint16_t alloc; /* 记录了当前字节数组总共分配的内存大小 */
    unsigned char flags; /* 记录当前字节数组的属性,究竟是哪一个SDS结构 */
    char buf[];/* 保存字符串真正的值 */
};
//字符串长度小于4294967296 Byte (4GB),大于65536 Byte
struct __attribute__ ((__packed__)) sdshdr32 {
    uint32_t len; /* 记录当前数组的长度 */
    uint32_t alloc; /* 记录了当前字节数组总共分配的内存大小 */
    unsigned char flags; /* 记录当前字节数组的属性,究竟是哪一个SDS结构 */
    char buf[];/* 保存字符串真正的值 */
};
//字符串长度大于4294967296 Byte (4GB)
struct __attribute__ ((__packed__)) sdshdr64 {
    uint64_t len; /* 记录当前数组的长度 */
    uint64_t alloc; /* 记录了当前字节数组总共分配的内存大小 */
    unsigned char flags; /* 记录当前字节数组的属性,究竟是哪一个SDS结构 */
    char buf[];/* 保存字符串真正的值 */
};

链表

链表是redis对象列表(list)的一种实现。当列表中元素数量比较多,或元素中字符串数量比较大时,就会使用链表结构。链表方便顺序查询,同时也方便插入和删除。数组

链表由listNode和list组成,其结构以下数据结构

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);
    //对比函数
    int (*match)(void *ptr, void *key);
}

结构图以下:函数

字典

字典能够用来保存键值对,字典的结构和java的hashMap结构类似,采用hash算法保存key。字典由字典,哈希表,哈希表节点实现。其结构以下:ui

/*
 * 哈希表节点
 */
typedef struct dictEntry {
    // 键
    void *key;
    // 值,能够是三种形式:指针,8字节字符,8字节整数
    union {
        void *val;
        uint64_t u64;
        int64_t s64;
    } v;
    //指向下个哈希表节点,造成链表(以下hash冲突,会造成链表)
    struct dictEntry *next;
}
/*
 * 哈希表
 */
typedef struct dictht {
    // 哈希表数组
    // 里面存储dictEntry
    dictEntry **table;
    // 哈希表大小
    unsigned long size;
    // 哈希表大小掩码,用于计算索引值
    // 老是等于 size - 1,index = hash & sizemask
    // 这样全部计算得出的hash值均可以对应于table的索引
    unsigned long sizemask;
    // 该哈希表已有节点的数量
    unsigned long used;
}
/*
 * 字典
 */
typedef struct dict {
    // 类型特定函数,type和privdata是为了多态字典所设置的
    dictType *type;
    // 私有数据
    void *privdata;
    // 哈希表,每一个字典包含两个table
    // ht[0]是平时使用的哈希表,ht[1]是rehash时使用的哈希表
    dictht ht[2];
    // rehash 索引标识
    // 当 rehash 不在进行时,值为 -1
    int rehashidx; 
}

字典结构图以下:编码

跳跃表

跳跃表是一种有序的数据结构,它经过score进行排序。跳跃表有层级的概念,每一个跳跃表节点有多层,每一个层级会按顺序指向以后若干节点的对应层级。这是为了便于查询,经过层级查询节点,会比按score顺序依次查询要快不少。节点和链表的结构以下:

typedef struct zskiplistNode{
   //层
     struct zskiplistLevel{
     //前进指针
        struct zskiplistNode *forward;
    //跨度
        unsigned int span;
    } level[];
  //后退指针
    struct zskiplistNode *backward;
  //分值
    double score;
  //成员对象
    robj *obj;
}
typedef struct zskiplist {
     //表头节点和表尾节点
     struct zskiplistNode *header,*tail;
     //表中节点数量
     unsigned long length;
     //表中层数最大的节点的层数
     int level;
}

结构图以下:

整数集合

当一个集合的元素数量不大,且类型都为整数时,redis就会使用整数集合进行存储。它的结构以下:

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

压缩列表

压缩列表是列表对象和哈希对象的实现方式之一。当一个列表的元素数量很少,且值为整数和不长的字符串,这时redis会使用压缩列表。一样若是一个哈希对象的key和value的值数量很少,且为整数和不长的字符串时,redis也会使用压缩列表

 

参考资料:

http://www.javashuo.com/article/p-abiguhhk-bm.html

https://www.cnblogs.com/jaycekon/p/6277653.html

相关文章
相关标签/搜索