Redis的幾種底層數據結構

Redis的幾種底層數據結構

簡單字符串

  • Redis的字符串是本身構建的一種名爲簡單動態字符串(SDS)的抽象類型。 和常規c語言字符串不一樣segmentfault

  • SDS的定義以下:數組

struct sdshdr{
    int len; //記錄buf數組中已使用字節的數量 既SDS所保存字符串長度
    int free; // 記錄buf數組中未使用字節的數量
    char buf[]; //字節數組,用於保存字符串
};

如圖所示 存儲 「Redis 」這個字符串安全

  • SDS 能夠常數獲取字符串長度,裏面的len記錄的字符串的長度,因此能夠直接獲取。而c語言須要O(n)的複雜度
  • SDS能夠杜絕緩衝區溢出。當API對SDS進行修改時,API會先檢查SDS的空間是否知足修改需求若是不知足,會先擴展空間
  • 能夠減小修改字符串時帶來的內存分配次數。
    • 空間預分配。當SDS進行修改的時候,程序不只會爲SDS分配修改所必須的空間,還會爲SDS分配額外的未使用空間(free)
    • 惰性空間釋放。當SDS的API須要縮短SDS保存的字符串時,使用free屬性記錄下來,並不會去回收這些內存
  • 二進制安全。全部SDS API 都是二進制安全,API都會以處理二進制的方式來處理SDS存放在buf數組裏的數據。

鏈表

  • 鏈表提供了高效的節點重拍能力,以及順序性的節點訪問方式,而且能夠經過增刪節點來靈活地調整鏈表長度數據結構

  • 鏈表的結構:函數

    typedef struct listNode{
        // 前置節點
        struct listNode *prev;
        //後置節點
        struct listNode *next;
        //節點的值
        void *value;
    }listNode;
    
    typedef struct list{
        listNode *head; // 表頭節點
        listNode *tail; // 表尾節點
        unsigned long len; // 鏈表所包含的節點數量
        void *(*dup) (void *ptr) // 節點複製函數
        void (*free)(void *ptr)  // 節點值釋放函數
        int (*match)(void *ptr,void *key) //節點值對比函數
    }list;

字典

字典,又稱爲符號表,關聯數組或映射,是一種用於保存鍵值對的抽象數據結構 ,在字典中,一個key和一個value進行關聯,字典中的key是獨一無二的ui

  • 字典的實現:編碼

    1.哈希表:
      typedef struct dicht{
          dictEntry **table; // 哈希表數組
          unsigned long size;  // 哈希表大小
          unsigned long sizemask; // 哈希表大小掩碼,用於計算索引值,老是等於size-1
            unsigned long used; // 該哈希表已有的節點數量
      }dicht;
    2.哈希表節點
      typedef struct dicEntry{
          void *key; //鍵
          union{    // 值
              void *val;
              uint64_t u64;
              int64_t s64;
          }v;
          struct dicEntry *next; // 指向下個哈希表節點,造成鏈表
      }dicEntry;
    3. 字典
      typedef struct dict{
          dictType *type; //類型特定函數
          void *privdata; //私有數據
          dictht ht[2];  // 哈希表
          int trehashidx;//rehash 索引,當rehash再也不進行時 值爲-1
      }dict;
      typedef struct dictType{
            // 計算哈希值的函數
          unsigned int (*hashFunction)(const void *key);
            //複製鍵的函數
            void *(*keyDup)(void *privdata,const void *key);
            // 複製值的函數
            void *(*valDup)(void *privdata,const void *obj);
            // 對比鍵的函數
            int (*keyCompare)(void *privdata,const void *key1 ,const void *key2);
            //銷燬鍵的函數
            void (*keyDestructor)(void *privdata,const void *key);
            //銷燬值的函數
            void (*valDestructor)(void *privdata,const void *obj);
      }dictType;

    圖示:指針

  • 解決鍵衝突問題。當有兩個或以上數量的鍵被分配到哈希表數組的同一個因此上面時,咱們採用鏈地址法來解決鍵衝突code

圖示:

  • rehash。隨着操做的執行,哈希表保存的鍵值對會逐漸曾多或者減小,爲了維護哈希表在一個合理範圍內,進行rehash操做

    • 爲字典的ht[1] 哈希表分配空間
    • 將保存在ht[0]的全部鍵值對從新計算哈希值和索引值,而後將鍵遷移到ht[1]
    • ht[0]包含的全部鍵值對都遷移到ht[1]以後(ht[0]變爲空)釋放ht[0],將ht[1]設置爲ht[0],而且在ht[1]新建立一個空白哈希表爲下一次rehash 作準備
  • 漸進式rehash。rehash的動做不是一次性集中式的完成 ,而是分屢次,漸進式的完成。

跳躍表

跳躍表是一種有序數據結構,經過每一個節點中維持多個指向其餘節點的指針,從而到達快速到達訪問節點的目的

整數集合

整數集合是集合鍵的底層實現之一,當一個集合只包含整數值元素,而且這個集合的元素數量很少的時候,Redis會使整數集做爲集合鍵的底層實現

  • 集合的實現:

    typedef struct intset{
        // 編碼方式
        uint32_t encoding;
        // 集合包含的元素數量
        uint32_t length;
        // 保存元素的數組
        int8_t contents[];
    }intset;

    圖示:

  • 升級。 每當咱們將一個新元素添加到整數集合,而且新元素的類型比整數集合現有的元素類型要長時,咱們要進行升級

  • 升級的好處:提高靈活性,節約內存

  • 整數集合不支持降級操做,一旦升級,編碼就會一直保持升級後的狀態

壓縮列表

壓縮列表是列表鍵和哈希鍵的底層實現之一

  • 壓縮列表的構成:

    壓縮列表是爲了節約內存而開發的,是由一系列特殊編碼的連續內存塊組成的順序型數據結構

    如圖所示:

  • previous_entry_length。節點的previous_entry_length屬性以字節爲單位,記錄了壓縮列表中前一點的長度,從表位向前遍歷壓縮列表時只用擁有一個只想某個節點的起始地址指針,經過這個指針以及這個節點的 previous_entry_length程序就能夠向前一個節點回溯

  • encoding。節點的encoding屬性記錄了節點的content屬性所保存數據的類型以及長度

  • content。節點的content負責保存節點的值

  • 連鎖更新。若是當前列表的節點e1到eN的節點長度都小於254字節,若是咱們將一個長度大於等於254字節的新節點設置爲壓縮列表的表頭時程序會不斷地對壓縮列表執行空間重分配操做直到eN位置,操做成爲「連鎖更新」

相關文章
相關標籤/搜索