Redis中有序集合與列表佔用記憶體分析
在說正題之前需要先瞭解幾種定義:字典、壓縮列表與跳躍表。
字典:非常常見的資料結構,key-value結構。
常見的實現有紅/黑樹狀結構(stl中的map),雜湊表(stl中的unordered_map)。紅/黑樹狀結構的尋找操作具有O(logN)的時間複雜度。雜湊表的尋找操作具有O(1)的時間複雜度。 redis中的字典使用雜湊表作為底層實現。
壓縮列表:由一些列特殊編碼的連續記憶體塊組成的順序型資料結構。
壓縮列表可以包含多種節點(只能儲存一種的那叫數組)。 壓縮列表的優點是節省記憶體。順序結構擁有的缺點壓縮列表全都有。
跳躍表:一種有序資料結構,它通過在每個節點中維護多個指向其他節點的指標,從而達到快速存取節點的目的。
跳躍表支援平均O(logN)、最壞O(N)時間複雜度的節點尋找。
redis中的跳躍表由zskiplist和zskiplistNode兩個結構組成,其中zskiplist用於儲存跳躍表的資訊(比如表前端節點、表尾節點、長度),而zskiplistNode用於表示跳躍表節點。跳躍表中的節點按照分支大小進行排序,當分值相同時,節點按照成員對象的大小進行排序。在同一跳躍表中,多個節點可以包含相同的分值,但每節點的成員對象必須是唯一的。
進入正題,為什麼redis中的有序集合佔用記憶體比列表大?
先說redis中的列表的實現,redis中的列表底層使用壓縮列表或鏈表來實現。redis列表有兩種不同的編碼(實現方式):ziplist和linkedlist。在特定的條件下,編碼格式可以進行相互轉換。當列表對象儲存的所有字串元素的長度都小於64位元組,並且列表對象的元素數量小於512時,列表對象使用ziplist。反之,使用linkedlist編碼。
重點說一下有序集合的實現,redis中有序集合的實現要更加複雜,包含ziplist和skiplist兩種不同編碼。
ziplist編碼的有序集合對象使用壓縮列表作為底層實現。每個集合元素使用兩個緊挨在一起的壓縮列表節點來儲存,第一個節點儲存元素的成員(member),而第二個元素則儲存元素的分值(score)。
skiplist編碼的有序集合對象使用zset結果作為底層實現,一個zset結構同時包含一個字典和一個跳躍表。zset結構中的跳躍表按照分值從小到大儲存了所有的集合元素,通過這個跳躍表,可以有序結合進行範圍型操作,例如zrank、zrange。 zset結構中的字典儲存了有序集合中成員到分值的映射,通過這個字典,可以用O(1)的時間複雜度尋找成員的分值。雖然zset使用兩種資料結構來儲存資料,但這兩種資料結構使用指標來共用相同元素的成員和分值,所以並不會產生任何重複的成員或者分值。
當有序集合儲存的元素數量小於128個,並且所有元素成員的長度小於64位元組時,使用ziplist編碼。反之,使用skiplist編碼。
為什麼有序集合要同時使用跳躍表和字典來實現呢?
單獨使用字典時,尋找快,只需要O(1)的時間複雜度,但是範圍操作就需要對字典元素進行排序,完成這種排序至少需要O(NlogN)的時間複雜度,以及額外的O(N)的記憶體空間。
單獨使用跳躍表時,跳躍表執行範圍操作的優點會被保留,但是尋找的效率會下降,尋找的時間複雜度會從O(1)上升到O(logN)。
通過以上的分析可以看到,列表對象的實現相比有序集合對象的實現要簡單的多,沒有那麼多亂七八糟的事情。所以,有序集合會比列表佔用更多的記憶體。
Ubuntu 14.04下Redis安裝及簡單測試
Redis叢集明細文檔
Ubuntu 12.10下安裝Redis(圖文詳解)+ Jedis串連Redis
Redis系列-安裝部署維護篇
CentOS 6.3安裝Redis
Redis安裝部署學習筆記
Redis設定檔redis.conf 詳解
Redis 的詳細介紹:請點這裡
Redis 的:請點這裡
本文永久更新連結地址: