Redisbook學習筆記(3)數據類型之有序集合

REDIS_ZSET (有序集) 是ZADD 、ZCOUNT 等命令的操做對象, 它使用redis

REDIS_ENCODING_ZIPLIST 和REDIS_ENCODING_SKIPLIST 兩種方式編碼:服務器

wKioL1MUfyPQe1glAACz8W_JBgE061.jpg

編碼的選擇數據結構

在經過ZADD 命令添加第一個元素到空key 時,程序經過檢查輸入的第一個元素來決定該創ide

建什麼編碼的有序集。函數

若是第一個元素符合如下條件的話,就建立一個REDIS_ENCODING_ZIPLIST 編碼的有序集:編碼

服務器屬性server.zset_max_ziplist_entries 的值大於0 (默認爲128 )。spa

元素的member 長度小於服務器屬性server.zset_max_ziplist_value 的值(默認爲64指針

)。server

不然,程序就建立一個REDIS_ENCODING_SKIPLIST 編碼的有序集。對象

編碼的轉換

對於一個REDIS_ENCODING_ZIPLIST 編碼的有序集,只要知足如下任一條件,就將它轉換爲

REDIS_ENCODING_SKIPLIST 編碼:

ziplist 所保存的元素數量超過服務器屬性server.zset_max_ziplist_entries 的值

(默認值爲128 )

新添加元素的member 的長度大於服務器屬性server.zset_max_ziplist_value 的值

(默認值爲64 )

ZIPLIST 編碼的有序集

當使用REDIS_ENCODING_ZIPLIST 編碼時,有序集將元素保存到ziplist 數據結構裏面。

其中,每一個有序集元素以兩個相鄰的ziplist 節點表示,第一個節點保存元素的member 域,

第二個元素保存元素的score 域。

多個元素之間按score 值從小到大排序,若是兩個元素的score 相同,那麼按字典序對member

進行對比,決定那個元素排在前面,那個元素排在後面。

wKioL1MUgA-jnP_6AACKQ-6svr0735.jpg

雖然元素是按score 域有序排序的,但對ziplist 的節點指針只能線性地移動,因此在

REDIS_ENCODING_ZIPLIST 編碼的有序集中,查找某個給定元素的複雜度爲O(N) 。

每次執行添加/刪除/更新操做都須要執行一次查找元素的操做,所以這些函數的複雜度都不低

於O(N) ,至於這些操做的實際複雜度,取決於它們底層所執行的ziplist 操做。

SKIPLIST 編碼的有序集

當使用REDIS_ENCODING_SKIPLIST 編碼時,有序集元素由redis.h/zset 結構來保存:

/*
* 有序集
*/
typedef struct zset {
// 字典
dict *dict;
// 跳躍表
zskiplist *zsl;
} zset;

zset 同時使用字典和跳躍表兩個數據結構來保存有序集元素。

其中,元素的成員由一個redisObject 結構表示,而元素的score 則是一個double 類型的浮

點數,字典和跳躍表兩個結構經過將指針共同指向這兩個值來節約空間(不用每一個元素都複製

兩份)。

下圖展現了一個REDIS_ENCODING_SKIPLIST 編碼的有序集:

wKiom1MUgUmCXQhiAAGF3CPhasw285.jpg

經過使用字典結構,並將member 做爲鍵,score 做爲值,有序集能夠在O(1) 複雜度內:

檢查給定member 是否存在於有序集(被不少底層函數使用);

取出member 對應的score 值(實現ZSCORE 命令)。

另外一方面,經過使用跳躍表,能夠讓有序集支持如下兩種操做:

在O(logN) 指望時間、O(N) 最壞時間內根據score 對member 進行定位(被不少底層

函數使用);

範圍性查找和處理操做,這是(高效地)實現ZRANGE 、ZRANK 和ZINTERSTORE

等命令的關鍵。

經過同時使用字典和跳躍表,有序集能夠高效地實現按成員查找和按順序查找兩種操做。

相關文章
相關標籤/搜索