redis吸引不少人使用的一個重要的緣由,就是它對衆多數據類型的支持。包括string,hash,set,zset,list等五種數據對象。redis
其中zset用來保證數據的有序存儲,實現中,redis使用跳躍表和壓縮列表,做爲zset的底層實現。當元素數量比較多,或者元素成員是比較長的字符串時,底層實現採用跳躍表。算法
跳躍表是什麼?數組
一種有序的數據結構,經過在節點中維持多個指向其它節點的指針,達到快速訪問的目的。數據結構
跳躍表的好處是什麼? ide
1. 跟平衡樹相比,實現簡單;spa
2. 平均複雜度爲O(logN),最壞爲O(N);指針
跳躍表的數據結構由 zskiplistNode 和 zskiplist 兩個結構定義,zskiplistNode表示跳躍表節點,zskiplist保存跳躍表節點的相關信息。對象
zskiplistNode的數據結構以下:blog
typedef struct zskiplistNode {排序
robj *obj;
double score;
struct zskiplistNode *backward;
struct zskiplistLevel {
struct zskiplistNode *forward;
unsigned int span;
} level[];
} zskiplistNode
skiplistLevel包含多個元素,每一個元素包含一個指向其它節點的前進指針,經過它來加快訪問其它節點。每次建立一個跳躍表節點的時候,會隨機生成一個值,該值介於1和32之間,做爲level數組的大小。span表示層的跨度,用來計算最終的位置,程序在遍歷時,會將沿途訪問過的全部層的跨度累計,得出目標節點的對應位置;
backward用於從表尾向表頭訪問節點;
score 全部節點都按照它進行排序;
obj 指向一個字符串對象;
zskiplist的數據結構:
typedef struct zskiplist {
struct zskiplistNode *header, *tail;
unsigned long length;
int level;
} zskiplist;
header,tail用來表示跳躍表的表頭和表尾巴節點,length表示跳躍表的總體長度,level表示跳躍表的高度。
zskiplist的主要做用,是能夠方便的對整個跳躍表進行處理,好比獲取跳躍表的整個長度的信息。
跳躍表的層數如何生成?
在插入的過程當中構造,向跳躍表中插入一個數值,至關於在表中插入一列從起始跳躍表節點S出發,向上的一段數值,須要肯定兩個元素:數值的位置和層數。因爲全部鏈是遞增序列的方式,因此位置主要是根據對應數值的比較對出。而層數,程序會先記錄比該節點小的值的span,而後,隨機生成介於1和32之間的數值,做爲該節點的層數,該算法主要是基於冥次定律,越大的數出現的機率越小。假設level爲2的機率爲P,則level爲3的機率爲p*p,以此類推。
跳躍表查找的示例圖:
圖中,表示有三個節點,他們對應的score分別爲1,2,3,其中,虛線,表明在跳躍表中,查找score爲2,成員對象爲02的節點,在查找過程當中,通過了兩個span爲1的節點,因此,能夠得出該節點在跳躍表中的排位爲2。 BW表示後退指針,實線的,表示節點間的span,因爲節點間的關係比較多,因此只畫了幾個。