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

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

REDIS_ENCODING_ZIPLIST 和REDIS_ENCODING_SKIPLIST 兩種方式編碼:

wKioL1MUfyPQe1glAACz8W_JBgE061.jpg

編碼的選擇

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

建什麼編碼的有序集。

如果第一個元素符合以下條件的話,就創建一個REDIS_ENCODING_ZIPLIST 編碼的有序集:

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

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

)。

否則,程序就創建一個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

等命令的關鍵。

通過同時使用字典和跳躍表,有序集可以高效地實現按成員查找和按順序查找兩種操作。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章