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

相關文章
相關標籤/搜索