Redis(一) -- 淺談Redis中的數據結構

一:摘要概述

redis的使用者均可以如數家珍的掏出Redis中經常使用的對象如string、list、hash、set、zset,一些場景比較豐富的使用者可能會說布隆過濾器、geoHash等。可是對於這些對象底層實現的數據結構倒是知之甚少,本文做爲redis學習第一篇文章,將會詳細闡述redis中的底層數據結構redis

二:SDS

string做爲redis中經常使用對象之一,廣泛用於用戶信息緩存等場景。當string對象中encoding編碼爲embstr或raw時都是採用sds做爲其底層實現數組

2.1 SDS結構

源碼文件位於redis安裝目錄src下的sds.h,sds聲明瞭五種頭部類型,分別爲sdshdr五、sdshdr八、sdshdr1六、sdshdr3二、sdshdr64。根據字符串長度建立不一樣頭部的sds實例緩存

struct __attribute__ ((__packed__)) sdshdr8 {
    uint8_t len; 
    uint8_t alloc;
    unsigned char flags;
    char buf[];
};
複製代碼
屬性名稱 做用含義
len 字符串長度
alloc 預分配空間大小
flags 低三位用於表示sds類型,能夠查看sds.h文件76-82行定義
buf[] 存儲字符串用數組

2.2 SDS與C字符串區別

區別 描述
長度計算 c中的字符串長度計算須要數組遍歷,可是redis中的sds自身維護了len屬性。因此O(1)時間複雜度便可
緩衝區溢出 c中字符串更改若是未提早作好內存分配則會內存溢出,可是sds則會根據alloc與len計算預留內存是否足夠分配從新申請內存
動態擴展 緩衝區溢出已經闡述這個概念,sds的內存空間會在字符串內容變動時自動擴展計算。策略爲當字符換小於1M時*2翻倍,大於1M時每次擴容1M
惰性釋放 與空間預分配類似操做的還有內存惰性釋放,即字符串刪除某些內容後所佔用的內存空間並不會當即釋放,後續字符串變動擴展就無需再申請內存

二:ZipList

ziplist能夠說把redis對於內存的極致操做體現的淋漓盡致,鏈表除了節點值以外還須要維護先後節點兩個指針,而且還會形成內存碎片。壓縮列表緊湊的內存佈局,全部節點都維護在整塊內存中處理 bash

2.1 ZipList結構

屬性名稱 做用含義
zlbytes 列表健佔用內存的總字節數,在對列表健內存重分配或者是計算zlend的時候使用
zltail 指向壓縮列表起始地址的指針
zllen 壓縮列表的節點數量
entry 壓縮列表保存的節點數據
zlend 壓縮列表的尾節點

2.2 Entry節點結構

屬性名稱 做用含義
previous_entry_length 字節爲單位記錄上一個節點的長度,若是上一個字節長度小於254佔用1字節。大於254佔用5字節,第一個字節設置爲OxFE(十進制254),後面四個字節儲存長度
encoding 記錄content記錄的數據類型以及長度。長度1、2、五字節,值的最高位爲00、0一、10表示類型爲字節數組,長度使用除去最高位的其它位記錄。11開頭表示儲存整數,除去最高位其餘位置表示content數據長度
content 記錄壓縮列表記錄的數據

2.3 連鎖更新

一個壓縮列表節點在保存上一個節點長度使用previous_entry_length屬性,這個屬性可使用1字節或者是5字節。假設現有一個壓縮列表裏面保存的節點長度所有都是250-253,這時候previous_entry_length使用一字節記錄就行。可是這時候添加一個新節點到頭節點的位置,剛好這個節點的大小大於254字節,這時候全部後面字節都須要更新,由於他們的previous_entry_length都會變成5字節數據結構

三:QuickList

list鏈表是redis中經常使用對象之一,以前一些版本中底層編碼數據採用雙向鏈表、壓縮列表的數據結構。可是後續考慮鏈表指針維護開銷以及內存碎片緣由,開發新的數據結構quicklist,這是一個雙向鏈表和壓縮列表的混合體函數

3.1 quicklist圖示

3.2 結構描述

typedef struct quicklist {
    quicklistNode *head;
    quicklistNode *tail;
    unsigned long count;
    unsigned long len;          
    int fill : 16;             
    unsigned int compress : 16;
} quicklist;
複製代碼
屬性名稱 做用含義
head 頭部節點
tail 尾部節點
count 壓縮列表元素數量總數
len ziplist節點數量
fill 單個ziplist節點的填充因子
compress 不壓縮節點的深度

3.3 ziplist節點

quicklist 內部默認單個 ziplist 長度爲 8k字節,超出了這個字節數就會新建一個 ziplist。ziplist 的長度由配置參數 list-max-ziplist-size決定佈局

3.4 LZF壓縮

快速列表ziplist爲了push與pop操做的效率默認首尾節點不進行LZF壓縮,若是須要設置更多節點不進行LZF壓縮能夠經過redis.conf配置文件中1099行list-compress-depth 0參數定義性能

四:Dict

redis中的hash、set等對象都有使用到字典這個數據結構,字典底層實現使用哈希表的結構。字典中主要掌握它的漸進式hash,結構源碼位置位於dict.h文件中學習

4.1 字典結構

typedef struct dict {
    dictType *type;
    void *privdata;
    dictht ht[2];
    long rehashidx;
} dict;
複製代碼
屬性名稱 做用含義
type 自定義一些操做的方法,拷貝key、拷貝value、銷燬key、銷燬value等
privdate 建立dict時傳入,用於某些特殊操做回傳給調用函數
ht [0]用於數據存儲,[1]用於rehash變動
rehashidx 表示rehash進度,-1表示未進行rehash

4.2 哈希表結構

typedef struct dictht {
    dictEntry **table;
    unsigned long size;
    unsigned long sizemask;
    unsigned long used;
} dictht;
複製代碼
屬性名稱 做用含義
table hash表節點
size hash表大小
sizemark 哈希表大小掩碼,計算索引值。大小等於size -1
used 哈希表已有的節點數量

4.3 哈希表節點結構

typedef struct dictEntry{
    void *key;
    union{
        void *val;
        uint64_tu64;
        int64_ts64;
    }v;
    struct dictEntry *next;
}dictEntry
複製代碼
屬性名稱 做用含義
key 保存數據的key值
union 值對象,能夠是一個對象,由於有個對象空指針或者是uint6四、int64的整數
next 指向下一個Entry的指針,造成一個鏈表

4.4 漸進式rehash

  • 字典的rehash操做數據量過大時並非一次完成,而是分批次逐漸進行
  • rehash過程當中新插入字典數據放在[1]哈希表中,並將原[0]中數據從新進行hash計算加入[1]中。讀操做將會讀取[0]、[1]兩個哈希表
  • rehash過程標誌使用dict中屬性rehashidx標識
  • rehash採用cow寫時複製技術

五:Intset

redis中經常使用對象set會用到的底層數據結構ui

5.1 整數集合特色

  • 1:內容全是數字
  • 2:內存連續
  • 3:元素有序,不可重複

5.2 Intset結構

typedef struct intset{
    uint32_t encoding;
    uint32_t length;
    int8_t contents[];
}intset;
複製代碼
屬性名稱 做用含義
encoding 整數集合能夠有三種編碼方式1六、3二、64
length 整數集合數組中保存的元素個數
contents 從小到大保存的整數集合中的元素

六:ZipList

zset中用到的一個數據結構,查詢快是真的,性能能夠和紅黑樹、AVL樹不相上下

6.1 跳躍表結構

typedef struct zskiplist{
    //表頭結點和尾節點
    structz skiplistNode *heade,*tail;
    //表中節點數量
    unsigned long length;
    //表中層數最大的節點的層數
    int level;
}zskiplist;
複製代碼
屬性名稱 做用含義
head 跳躍表頭結點
tail 跳躍表尾節點
length 跳躍表節點數量,表頭結點不記錄在裏面
level 跳躍表最大層數,不記錄表頭節點

6.2 跳躍表節點

typedof struct zskiplistNode{
    //層
    struct zskiplistNode{
        //前進指針
        struct zskiplistNode *forward;
        //跨度
        unsihned int span;
    }level[];
    //後退指針
    struct zskiplistNode *backward;
    //分值
    double score;
    //成員對象
    robj *obj;
}zsikplistNode;
複製代碼
屬性名稱 做用含義
zskiplistNode 集合記錄該節點位於的每一層
forward 每一層節點對應的下一個節點
span 距離下一個節點須要跨越的層數
backward 後退指針
score 節點分數值
obj 跳躍表節點保存的對象
相關文章
相關標籤/搜索