redis的使用者均可以如數家珍的掏出Redis中經常使用的對象如string、list、hash、set、zset,一些場景比較豐富的使用者可能會說布隆過濾器、geoHash等。可是對於這些對象底層實現的數據結構倒是知之甚少,本文做爲redis學習第一篇文章,將會詳細闡述redis中的底層數據結構redis
string做爲redis中經常使用對象之一,廣泛用於用戶信息緩存等場景。當string對象中encoding編碼爲embstr或raw時都是採用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[] | 存儲字符串用數組 |
區別 | 描述 |
---|---|
長度計算 | c中的字符串長度計算須要數組遍歷,可是redis中的sds自身維護了len屬性。因此O(1)時間複雜度便可 |
緩衝區溢出 | c中字符串更改若是未提早作好內存分配則會內存溢出,可是sds則會根據alloc與len計算預留內存是否足夠分配從新申請內存 |
動態擴展 | 緩衝區溢出已經闡述這個概念,sds的內存空間會在字符串內容變動時自動擴展計算。策略爲當字符換小於1M時*2翻倍,大於1M時每次擴容1M |
惰性釋放 | 與空間預分配類似操做的還有內存惰性釋放,即字符串刪除某些內容後所佔用的內存空間並不會當即釋放,後續字符串變動擴展就無需再申請內存 |
ziplist能夠說把redis對於內存的極致操做體現的淋漓盡致,鏈表除了節點值以外還須要維護先後節點兩個指針,而且還會形成內存碎片。壓縮列表緊湊的內存佈局,全部節點都維護在整塊內存中處理 bash
屬性名稱 | 做用含義 |
---|---|
zlbytes | 列表健佔用內存的總字節數,在對列表健內存重分配或者是計算zlend的時候使用 |
zltail | 指向壓縮列表起始地址的指針 |
zllen | 壓縮列表的節點數量 |
entry | 壓縮列表保存的節點數據 |
zlend | 壓縮列表的尾節點 |
屬性名稱 | 做用含義 |
---|---|
previous_entry_length | 字節爲單位記錄上一個節點的長度,若是上一個字節長度小於254佔用1字節。大於254佔用5字節,第一個字節設置爲OxFE(十進制254),後面四個字節儲存長度 |
encoding | 記錄content記錄的數據類型以及長度。長度1、2、五字節,值的最高位爲00、0一、10表示類型爲字節數組,長度使用除去最高位的其它位記錄。11開頭表示儲存整數,除去最高位其餘位置表示content數據長度 |
content | 記錄壓縮列表記錄的數據 |
一個壓縮列表節點在保存上一個節點長度使用previous_entry_length屬性,這個屬性可使用1字節或者是5字節。假設現有一個壓縮列表裏面保存的節點長度所有都是250-253,這時候previous_entry_length使用一字節記錄就行。可是這時候添加一個新節點到頭節點的位置,剛好這個節點的大小大於254字節,這時候全部後面字節都須要更新,由於他們的previous_entry_length都會變成5字節數據結構
list鏈表是redis中經常使用對象之一,以前一些版本中底層編碼數據採用雙向鏈表、壓縮列表的數據結構。可是後續考慮鏈表指針維護開銷以及內存碎片緣由,開發新的數據結構quicklist,這是一個雙向鏈表和壓縮列表的混合體函數
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 | 不壓縮節點的深度 |
quicklist 內部默認單個 ziplist 長度爲 8k字節,超出了這個字節數就會新建一個 ziplist。ziplist 的長度由配置參數 list-max-ziplist-size
決定佈局
快速列表ziplist爲了push與pop操做的效率默認首尾節點不進行LZF壓縮,若是須要設置更多節點不進行LZF壓縮能夠經過redis.conf配置文件中1099行list-compress-depth 0
參數定義性能
redis中的hash、set等對象都有使用到字典這個數據結構,字典底層實現使用哈希表的結構。字典中主要掌握它的漸進式hash,結構源碼位置位於dict.h文件中學習
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 |
typedef struct dictht {
dictEntry **table;
unsigned long size;
unsigned long sizemask;
unsigned long used;
} dictht;
複製代碼
屬性名稱 | 做用含義 |
---|---|
table | hash表節點 |
size | hash表大小 |
sizemark | 哈希表大小掩碼,計算索引值。大小等於size -1 |
used | 哈希表已有的節點數量 |
typedef struct dictEntry{
void *key;
union{
void *val;
uint64_tu64;
int64_ts64;
}v;
struct dictEntry *next;
}dictEntry
複製代碼
屬性名稱 | 做用含義 |
---|---|
key | 保存數據的key值 |
union | 值對象,能夠是一個對象,由於有個對象空指針或者是uint6四、int64的整數 |
next | 指向下一個Entry的指針,造成一個鏈表 |
redis中經常使用對象set會用到的底層數據結構ui
typedef struct intset{
uint32_t encoding;
uint32_t length;
int8_t contents[];
}intset;
複製代碼
屬性名稱 | 做用含義 |
---|---|
encoding | 整數集合能夠有三種編碼方式1六、3二、64 |
length | 整數集合數組中保存的元素個數 |
contents | 從小到大保存的整數集合中的元素 |
zset中用到的一個數據結構,查詢快是真的,性能能夠和紅黑樹、AVL樹不相上下
typedef struct zskiplist{
//表頭結點和尾節點
structz skiplistNode *heade,*tail;
//表中節點數量
unsigned long length;
//表中層數最大的節點的層數
int level;
}zskiplist;
複製代碼
屬性名稱 | 做用含義 |
---|---|
head | 跳躍表頭結點 |
tail | 跳躍表尾節點 |
length | 跳躍表節點數量,表頭結點不記錄在裏面 |
level | 跳躍表最大層數,不記錄表頭節點 |
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 | 跳躍表節點保存的對象 |