《Redis核心技术与实战》学习总结(2)
【Redis】| 總結/Edison Zhou
1上一篇的遺留問題
上一篇總結了一個KV數據庫的基本架構 和 Redis的底層數據結構概覽,重點總結了Sorted Set的兩個數據結構的切換,但沒有介紹List的兩個數據結構的切換,因此本文試著總結一下。
這里先直接給出答案:
從上圖可以看到,當List的數據滿足下面兩個條件時,就會使用壓縮列表,否則使用雙向鏈表。
(1)列表對象保存的所有字符串元素的長度都小于64字節;
(2)列表對象保存的元素數量小于512個;
這兩個參數其實也是可以在redis.conf中修改的:
list-max-ziplist-value 64 list-max-ziplist-entries 5122Redis 3.2之前的實現
由上一篇已經知道,List類型的底層實現包括了 雙向鏈表 和?壓縮列表,但這是在Redis的3.2版本之前的底層實現。而從Redis 3.2版本開始,Redis修改了List的底層實現,將壓縮列表 和 雙向鏈表 結合,我們稱它為 quickList 快速列表。
從第一節的內容我們已經知道,當創建一個新的List時,Redis會優先使用壓縮列表,然后在有需要的時候,再轉成雙向鏈表。
Redis為什么要這么設計呢?
因為,雙向鏈表的內存占用 比 壓縮列表?多,而壓縮列表的設計初衷就在于 節約內存。眾所周知,Redis之所以快的原因之一就是它是內存數據庫,所有操作都在內存上完成,因此對于內存的占用有要求。
雙向鏈表
壓縮列表
畫外音:在Redis 3.2 之前,我們也可以通過命令來驗證:
192.168.80.100:6379>?rpush?testkey?"edison"?"andy"?"leo" 3 192.168.80.100:6379> object encoding testkey ziplist 192.168.80.100:6379> rpush testkey "wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww" 4 192.168.80.100:6379> object encoding testkey linkedlist那么,壓縮列表為什么占用內存少呢?
其實從上面的圖和下面的源碼也可以看出來,壓縮列表并沒有維護雙向指針prev 和 next,而只是存儲了上一個entry的長度 和 下一個entry的長度,通過長度來推算下一個entry在哪里。
typedef struct zlentry { // 壓縮列表節點unsigned int prevrawlensize, prevrawlen; // prevrawlen是前一個節點的長度,prevrawlensize是指prevrawlen的大小,有1字節和5字節兩種unsigned int lensize, len; // len為當前節點長度 lensize為編碼len所需的字節大小unsigned int headersize; // 當前節點的header大小unsigned char encoding; // 節點的編碼方式unsigned char *p; // 指向節點的指針 }?zlentry;這是一種典型的“時間換空間”的方法,即犧牲讀取的性能,換取極致的存儲空間。由于壓縮列表存儲在一段連續的內存上,所以它的存儲效率還是蠻高的。
但是,此種設計只適合在字段個數、值比較小的時候,一旦長度過長,壓縮列表的設計(利于讀取但不利于修改的初衷)會導致修改和刪除操作需要頻繁的申請和釋放內存,可能會導致大量的數據拷貝,拖慢Redis的整體性能。
因此,Redis選擇了在達到閾值時,切換數據結構為雙向鏈表。
3Redis 3.2之后的實現
在Redis 3.2及之后,Redis選擇了結合壓縮列表 和 雙向鏈表的優點,形成了一個新的底層實現:quicklist 快速列表。
快速列表是一個壓縮列表組成的雙向鏈表,每個節點使用壓縮列表來保存數據。換句話說,快速列表中保存了一個個小的壓縮列表。其結構如下圖所示:
為了進一步節約空間,Redis 還會對壓縮列表進行壓縮存儲(一種無損壓縮算法LZF),這取決壓縮深度的參數設置,我們可以選擇不壓縮(默認值不壓縮)?也可以 選擇壓縮中間節點。
畫外音:兩端節點一般不被壓縮,因為當一個鏈表很長時,最頻繁訪問的就是兩端的數據,根據“二八定律”,兩端數據不壓縮,而將中間數據壓縮,從而節省空間,但又保證讀取效率。
此外,對于每個壓縮列表的大小,也是可以通過在redis.conf中的參數來設置的:
list-max-ziplist-size -2參數可選值從-1到-5,其含義如下:
1) -5:每個quicklist節點上的ziplist大小不能超過64kb。
2) -4:每個quicklsit節點上的ziplist大小不能超過32kb。
3) -3:每個quicklsit節點上的ziplist大小不能超過16kb。
4) -2:每個quicklsit節點上的ziplist大小不能超過8kb。
5) -1:每個quicklsit節點上的ziplist大小不能超過4kb。
畫外音:在Redis 3.2 之后,我們也可以通過命令來驗證:
綜述,快速列表的本質其實是對壓縮列表的一次封裝,使用小塊的壓縮列表來組織,既可以保證內存占用較小,也可以保證操作性能。
End總結
本文總結了Redis的List類型在何時使用壓縮列表,何時使用雙向鏈表,以及快速列表的基本概念。當然,更多的內容還是需要自行去搜索學習,意猶未盡的童鞋也可以去分析源碼。最后,如果你對其他集合類型也有此類問題,你可以參考下面附錄中的內容,而至于Why,則可以自行百度搜索了解。
Anyway,對于Redis集合類型的底層思想采用了兩種數據結構的設計思想是值得我們學習借鑒的,它其實充分體現了軟件設計中的Tradeoff(權衡)思想。對于Redis來說,即在主體目標是保證性能的大約束前提下,權衡多方因素如操作時間和空間占用,以達到較為穩定的運行表現。對于軟件設計來說,也需要在時間 vs 空間,新技術 vs 老技術,優雅 vs 效率,輕度設計 vs 重度設計等之間做權衡,一個問題總會有多種解決方案可以實現,在特定的時間段,永遠沒有最完美的設計,只有較合適的設計。在實際中,它可能結合了多種因素的考慮,不斷地去粗取精,迭代為更好的設計。
Ref附錄
Hash:
Set:
Sorted Set(zset):
參考資料
極客時間,蔣德鈞《Redis核心技術與實戰》
年終總結:Edison的2020年終總結
數字化轉型:我在傳統企業做數字化轉型
C#刷題:C#刷劍指Offer算法題系列文章目錄
.NET面試:.NET開發面試知識體系
.NET大會:2020年中國.NET開發者大會PDF資料
??
?
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的《Redis核心技术与实战》学习总结(2)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SingnalR 开发到生产部署闭坑指南
- 下一篇: 《Redis核心技术与实战》学习总结(1