面試官:Redis中有序集合的內部實現方式是什麼?

萬貓學社發表於2022-03-13

面試官:Redis中基本的資料型別有哪些?

我:Redis的基本資料型別有:字串(string)、雜湊(hash)、列表(list)、集合(set)、有序集合(zset)。

面試官:有序集合的內部實現方式是什麼?

我還沉浸在上一個問題的沾沾自喜中,頓時表情凝固了,手心開始冒出冷汗。“這個。。沒有太深入瞭解”,我支支吾吾的說到。

面試官:回去等訊息吧。

這句話說的乾淨利落,然後就沒有然後了。失敗是成功的媽媽,我不氣餒,決定馬上惡補一下。

有序集合的內部實現

有序集合的內部實現有兩種,分別是:壓縮列表(ziplist)和跳躍表(skiplist)。接下來,我們分別進行詳細的瞭解。

以壓縮列表作為內部實現

當有序集合的元素個數小於zset-max-ziplist-entries(預設為128個),並且每個元素成員的長度小於zset-max-ziplist-value(預設為64位元組)的時候,使用壓縮列表作為有序集合的內部實現。

每個集合元素由兩個緊挨在一起的兩個壓縮列表結點組成,其中第一個結點儲存元素的成員,第二個結點儲存元素的分支。壓縮列表中的元素按照分數從小到大依次緊挨著排列,有效減少了記憶體空間的使用。

舉個例子,我們使用zadd命令建立一個以壓縮列表為實現的有序集合:

127.0.0.1:6379> zadd one-more-zset 1 one 2 two 3 three
(integer) 3
127.0.0.1:6379> zrange one-more-zset 0 -1
1) "one"
2) "two"
3) "three"
127.0.0.1:6379> object encoding one-more-zset
"ziplist"

以跳躍表作為內部實現

當有序集合的元素個數大於等於zset-max-ziplist-entries(預設為128個),或者每個元素成員的長度大於等於zset-max-ziplist-value(預設為64位元組)的時候,使用跳躍表作為有序集合的內部實現。

此時,在有序集合中其實包含了兩個結構,一個是跳躍表,另一個是雜湊表。

在跳躍表中,所有元素按照從小到大的順序排列。跳躍表的結點中的object指標指向元素成員的字串物件,score儲存了元素的分數。通過跳躍表,Redis可以快速地對有序集合進行分數範圍、排名等操作。

在雜湊表中,為有序集合建立了一個從元素成員到元素分數的對映。鍵值對中的鍵指向元素成員的字串物件,鍵值對中的值儲存了元素的分數。通過雜湊表,Redis可以快速查詢指定元素的分數。

雖然有序集合同時使用跳躍表和雜湊表,但是這兩種資料結構都使用指標共享元素中的成員和分數,不會額外的記憶體浪費。

舉個例子,我們使用zadd命令建立一個以跳躍表為實現的有序集合:

127.0.0.1:6379> zadd one-more-zset 1 long-long-long-long-long-long-long-long-long-long-long-long-long-long
(integer) 1
127.0.0.1:6379> zrange one-more-zset 0 -1
1) "long-long-long-long-long-long-long-long-long-long-long-long-long-long"
127.0.0.1:6379> object encoding one-more-zset
"skiplist"

內部實現的轉換

當一個有序集合是以壓縮列表作為內部實現時,再向這個有序集合新增較長的元素成員,或向這個有序集合的元素個數過多時,那麼這個有序集合就會轉換為以跳躍表作為內部實現。但是,以跳躍表作為內部實現的有序集合不會轉換為以壓縮列表作為內部實現。

舉個例子,我們先建立一個以壓縮列表作為內部實現的有序集合:

127.0.0.1:6379> zadd one-more-zset 1 one 2 two 3 three
(integer) 3
127.0.0.1:6379> zrange one-more-zset 0 -1
1) "one"
2) "two"
3) "three"
127.0.0.1:6379> object encoding one-more-zset
"ziplist"

然後,再向它新增一個較長成員的元素,它就是轉換為以跳躍表作為內部實現:

127.0.0.1:6379> zadd one-more-zset 4 long-long-long-long-long-long-long-long-long-long-long-long-long-long
(integer) 1
127.0.0.1:6379> zrange one-more-zset 0 -1
1) "one"
2) "two"
3) "three"
4) "long-long-long-long-long-long-long-long-long-long-long-long-long-long"
127.0.0.1:6379> object encoding one-more-zset
"skiplist"

然後,再把那一個較長成員的元素從有序集合中移除,有序集合依然是以跳躍表作為內部實現:

127.0.0.1:6379> zrem one-more-zset long-long-long-long-long-long-long-long-long-long-long-long-long-long
(integer) 1
127.0.0.1:6379> zrange one-more-zset 0 -1
1) "one"
2) "two"
3) "three"
127.0.0.1:6379> object encoding one-more-zset
"skiplist"

總結

在Redis中,有序集合的內部實現有壓縮列表(ziplist)和跳躍表(skiplist)兩種,當集合中的所有元素的成員長度較短並元素個數較少時,使用壓縮列表作為內部實現,否則使用跳躍表和雜湊表作為內部實現。當條件不滿足時,壓縮列表可以轉換為跳躍表,但跳躍表不能轉換為壓縮列表。

最後,謝謝你這麼帥,還給我點贊關注

微信公眾號:萬貓學社

微信掃描二維碼

關注後回覆「電子書」

獲取12本Java必讀技術書籍

面試官:Redis中有序集合的內部實現方式是什麼?

相關文章