跳躍表(skiplist)是一種有序數據結構,它經過在每一個節點中維持多個指向其餘節點的指針,從而達到快速訪問節點的目的。redis
redis使用跳躍表做爲有序集合的底層實現之一,若是一個有序集合包含的元素數量比較多,又或者有序集合中元素的成員是比較長的字符串時,redis就會使用跳躍表來做爲有序集合鍵的底層實現。redis只在兩個地方用到跳躍表,一個是實現有序集合鍵,另外一個是在集羣節點中用做內部數據結構。數組
跳躍表由redis.h/zskiplistNode和redis.h/zskiplist兩個結構定義,其中zskiplistNode結構用於表示跳躍表節點,而zskiplist結構則用於保存跳躍表節點的相關信息。bash
typedef struct zskiplistNode{ //層 struct zskiplistLevel{ //前進指針 struct zskiplistNode *forward; //跨度 unsigned int span; } level[]; //後退指針 struct zskiplistNode *backward; //分值 double score; //成員對象 robj *obj; }zskiplistNode;
(1).層數據結構
跳躍表節點的level數組能夠包含多個元素,每一個元素都包含一個指向其餘節點的指針,程序能夠經過這些層加快訪問其餘節點的速度,通常來講,層的數量越多,訪問其餘節點的速度就越快。ui
每次建立一個新跳躍表節點的時候,程序根據冪次定律隨機生成一個介於1和32之間的值做爲level數組的大小,這個大小就是層的「高度」。編碼
(2).前進指針spa
(3).跨度3d
層的跨度用於記錄兩個節點之間的距離:指針
>兩個節點之間的跨度越大,那麼距離越遠;對象
>指向null的全部前進指針的跨度是0。
跨度是用來計算排位(rank)的。
(4).後退指針
和能夠一次跳過多個節點的前進指針不一樣,由於每一個節點只有一個後退指針,因此每次只能後退到前一個節點。
(5)分值和成員
節點的分值是一個double類型的浮點數,跳躍表的全部節點都是按分值從小倒大排序。
節點的成員對象是一個指針,它指向一個字符串對象,而字符串對象則保存着一個SDS。
在同一張跳躍表中,各個節點保存的成員必須是惟一的,可是多個節點保存的分值卻能夠是相同的:分值相同的節點將按照成員對象在字典序中的大小進行排序,小的排前面。
typedef struct zskiplist{ //層 struct zskiplistNode *header,*tail; //表中節點的數量 unsigned long length; //表中層數最大的節點的層數 int level; }zskiplist;
header和tail指針分別指向跳躍表的表頭和表尾節點,經過這兩個指針,程序定位表頭節點和表尾節點的複雜度是O(1)。經過length屬性記錄節點的數量。
整數集合是集合鍵的底層實現之一,當一個集合只包含整數值元素,而且這個集合的元素數量很少時,redis就會使用整數集合做爲集合鍵的底層實現。
typedef struct intset{ //編碼方式 uint32_t encoding; //集合包含的元素數量 uint32_t length; //保存元素的數組 int8_t contents[]; }intset;
contents數組是整數集合的底層實現:整數集合的每一個元素都是contents的一個數據項,每一個項在數組中按值的大小從小到大排列,而且數組中不包含任何重複項。
另外還有一點就是 假設一個新元素add到整數集合中,而且新元素的encoding類型比整數集合現有全部元素的類型都要長時,整數集合須要先進行升級,而後才能講新元素添加到整數集合中。一旦對數組進行了升級,沒法降級。
壓縮列表是列表鍵和哈希鍵的底層實現之一。當一個列表鍵只包含少許列表項,而且每一個列表項要麼就是小整數值,要麼就是長度比較短的字符串,那麼redis就會使用壓縮列表做爲列表鍵的底層實現。
redis>rpush lst 1 3 5 10086 "hello" "world" (integer) 6 redis>OBJECT ENCODING lst "ziplist"
另外,當一個哈希鍵只包含少許鍵值對,而且每一個鍵值對的鍵和值要麼就是小整數值,要麼就是長度比較短的字符串,那麼redis就會使用壓縮列表來作哈希鍵的底層實現。
redis>hmset profile "name" "Jack" "age" 28 "job" "teacher" OK redis>OBJECT ENCODING profile "ziplist"
壓縮列表是redis爲了節約內存而開發的,是由一系列特殊編碼的連續內存塊組成的順序型數據結構。
一個壓縮列表能夠包含任意多個節點(entry),每一個節點能夠保存一個字節數組或者一個整數值。
每一個壓縮列表節點均可以保存一個字節數組或者一個整數值。
(1)節點的previous_entry_length是以字節爲單位,記錄着前一節點的長度。經過這個屬性以及當前節點的起始值,能夠計算出上一節點的起始值。另外當前一節點的長度小於254字節,那麼previous_entry_length的長度是1字節,若是前一節點的長度大於等於254字節,那麼previous_entry_length的長度是5字節
(2)encoding記錄了節點content屬性所保存數據的長度和類型 :
>值的最高位是00,01,10的話,content保存的是字節數組;
>值的最高位是11的話,content保存的是整數。
當對壓縮列表進行新增或者刪除時,可能會發生連鎖更新(由於當前一節點變成大於254字節的時候,當前節點previous_entry_length會由1字節變成5字節,假設剛好當前節點此時也變成大於254字節,那麼當前節點的下一個節點的previous_entry_length也會變化....依次進行,產生了連鎖更新)