您好,登錄后才能下訂單哦!
本篇文章給大家分享的是有關Redis 有序集合對象底層實現是怎樣的,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
Redis 提供了5種數據類型:String(字符串)、Hash(哈希)、List(列表)、Set(集合)、Zset(有序集合),理解每種數據類型的特點對于redis的開發和運維非常重要。
<p align="right"><a href="http://www.yund.tech/zdetail.html?type=1&id=73b09694e9867b5808a4d8706f2f51b5">原文解析</a></p>
?因為有序集合鍵的值為有序集合對象,所以用于有序集合鍵的所有命令都是針對有序集合對象來構建的。
命令 | ziplist 編碼的實現方法 | zset 編碼的實現方法 |
---|---|---|
ZADD | 調用 ziplistInsert 函數, 將成員和分值作為兩個節點分別插入到壓縮列表。 | 先調用 zslInsert 函數, 將新元素添加到跳躍表, 然后調用 dictAdd 函數, 將新元素關聯到字典。 |
ZCARD | 調用 ziplistLen 函數, 獲得壓縮列表包含節點的數量, 將這個數量除以 2 得出集合元素的數量。 | 訪問跳躍表數據結構的 length 屬性, 直接返回集合元素的數量。 |
ZCOUNT | 遍歷壓縮列表, 統計分值在給定范圍內的節點的數量。 | 遍歷跳躍表, 統計分值在給定范圍內的節點的數量。 |
ZRANGE | 從表頭向表尾遍歷壓縮列表, 返回給定索引范圍內的所有元素。 | 從表頭向表尾遍歷跳躍表, 返回給定索引范圍內的所有元素。 |
ZREVRANGE | 從表尾向表頭遍歷壓縮列表, 返回給定索引范圍內的所有元素。 | 從表尾向表頭遍歷跳躍表, 返回給定索引范圍內的所有元素。 |
ZRANK | 從表頭向表尾遍歷壓縮列表, 查找給定的成員, 沿途記錄經過節點的數量, 當找到給定成員之后, 途經節點的數量就是該成員所對應元素的排名。 | 從表頭向表尾遍歷跳躍表, 查找給定的成員, 沿途記錄經過節點的數量, 當找到給定成員之后, 途經節點的數量就是該成員所對應元素的排名。 |
ZREVRANK | 從表尾向表頭遍歷壓縮列表, 查找給定的成員, 沿途記錄經過節點的數量, 當找到給定成員之后, 途經節點的數量就是該成員所對應元素的排名。 | 從表尾向表頭遍歷跳躍表, 查找給定的成員, 沿途記錄經過節點的數量, 當找到給定成員之后, 途經節點的數量就是該成員所對應元素的排名。 |
ZREM | 遍歷壓縮列表, 刪除所有包含給定成員的節點, 以及被刪除成員節點旁邊的分值節點。 | 遍歷跳躍表, 刪除所有包含了給定成員的跳躍表節點。 并在字典中解除被刪除元素的成員和分值的關聯。 |
ZSCORE | 遍歷壓縮列表, 查找包含了給定成員的節點, 然后取出成員節點旁邊的分值節點保存的元素分值。 | 直接從字典中取出給定成員的分值。 |
?由前文和上圖可知,有序集合的編碼可以是 ziplist 或者 skiplist 。ziplist 編碼的有序集合對象使用壓縮列表作為底層實現, 每個集合元素使用兩個緊挨在一起的壓縮列表節點來保存, 第一個節點保存元素的成員(member), 而第二個元素則保存元素的分值(score)。
壓縮列表內的集合元素按分值從小到大進行排序, 分值較小的元素被放置在靠近表頭的方向, 而分值較大的元素則被放置在靠近表尾的方向。
例如,如果我們執行以下 ZADD 命令, 那么服務器將創建一個有序集合對象作為 price 鍵的值:
redis> ZADD price 8.5 apple 5.0 banana 6.0 cherry (integer) 3
如果 price 鍵的值對象使用的是 ziplist 編碼, 那么這個值對象將會是圖 8-14 所示,, 而對象所使用的壓縮列表則會是 8-15 所示。 <center></center><center></center>
?skiplist 編碼的有序集合對象使用 zset 結構作為底層實現, 一個 zset 結構同時包含一個字典和一個跳躍表:
typedef struct zset { zskiplist *zsl; dict *dict; } zset;
zset 結構中的 zsl 跳躍表按分值從小到大保存了所有集合元素, 每個跳躍表節點都保存了一個集合元素: 跳躍表節點的 object 屬性保存了元素的成員, 而跳躍表節點的 score 屬性則保存了元素的分值。 通過這個跳躍表, 程序可以對有序集合進行范圍型操作, 比如 ZRANK 、ZRANGE 等命令就是基于跳躍表 API 來實現的。
除此之外, zset 結構中的 dict 字典為有序集合創建了一個從成員到分值的映射, 字典中的每個鍵值對都保存了一個集合元素: 字典的鍵保存了元素的成員, 而字典的值則保存了元素的分值。 通過這個字典, 程序可以用 O(1) 復雜度查找給定成員的分值, ZSCORE 命令就是根據這一特性實現的, 而很多其他有序集合命令都在實現的內部用到了這一特性。
有序集合每個元素的成員都是一個字符串對象, 而每個元素的分值都是一個 double 類型的浮點數。 值得一提的是, 雖然 zset 結構同時使用跳躍表和字典來保存有序集合元素, 但這兩種數據結構都會通過指針來共享相同元素的成員和分值, 所以同時使用跳躍表和字典來保存集合元素不會產生任何重復成員或者分值, 也不會因此而浪費額外的內存
skiplist 編碼的有序集合對象, 那么這個有序集合對象將會是圖 8-16 所示, 而對象所使用的 zset 結構將會是圖 8-17 所示。<center></center><center></center>
注意: ??為了展示方便, 圖 8-17 在字典和跳躍表中重復展示了各個元素的成員和分值, 但在實際中, 字典和跳躍表會共享元素的成員和分值, 所以并不會造成任何數據重復, 也不會因此而浪費任何內存。
當有序集合對象可以同時滿足以下兩個條件時, 對象使用 ziplist 編碼:
1.有序集合保存的元素數量小于 128 個;
2.有序集合保存的所有元素成員的長度都小于 64 字節;
不能滿足以上兩個條件的有序集合對象將使用 skiplist 編碼。
注意: ??以上兩個條件的上限值是可以修改的, 具體請看配置文件中關于 zset-max-ziplist-entries 選項和 zset-max-ziplist-value 選項的說明。對于使用 ziplist 編碼的有序集合對象來說, 當使用 ziplist 編碼所需的兩個條件中的任意一個不能被滿足時, 程序就會執行編碼轉換操作, 將原本儲存在壓縮列表里面的所有集合元素轉移到 zset 結構里面, 并將對象的編碼從 ziplist 改為 skiplist 。
1.以下情況展示了有序集合對象因為包含了過多元素而引發編碼轉換:
# 對象包含了 128 個元素 redis> EVAL "for i=1, 128 do redis.call('ZADD', KEYS[1], i, i) end" 1 numbers (nil) redis> ZCARD numbers (integer) 128 redis> OBJECT ENCODING numbers "ziplist" # 再添加一個新元素 redis> ZADD numbers 3.14 pi (integer) 1 # 對象包含的元素數量變為 129 個 redis> ZCARD numbers (integer) 129 # 編碼已改變 redis> OBJECT ENCODING numbers "skiplist"
2.以下情況展示了有序集合對象因為元素的成員過長而引發編碼轉換:
# 向有序集合添加一個成員只有三字節長的元素 redis> ZADD blah 1.0 www (integer) 1 redis> OBJECT ENCODING blah "ziplist" # 向有序集合添加一個成員為 66 字節長的元素 redis> ZADD blah 2.0 oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo (integer) 1 # 編碼已改變 redis> OBJECT ENCODING blah "skiplist"
1)有序集合的編碼可以是 ziplist 或者 skiplist。
2)一個 zset 結構同時包含一個字典和一個跳躍表。
3)zset 結構跳躍表和字典通過指針來共享相同元素的成員和分值。
4)有序集合對象 ziplist 或者 skiplist編碼,符合條件時可發生編碼轉換。
以上就是Redis 有序集合對象底層實現是怎樣的,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。