前言
Redis 已經是大家耳熟能詳的東西了,日常工作也都在使用,面試中也是高頻的會涉及到,那麼我們對它究竟瞭解有多深刻呢?
我讀了幾本 Redis 相關的書籍,嘗試去了解它的具體實現,將一些底層的數據結構及實現原理記錄下來。
本文將介紹 Redis 中底層的 listpack(緊湊列表) 的實現方法。 它是 Redis 的 Stream 用到的數據結構之一。
定義
Redis 設計 listpack 的目的就是取代 ziplist, 在 Redis 系列(三)底層數據結構之壓縮列表 中我們提到,ziplist 在極小的概率下有可能發生級聯更新,當連續規模較大的級聯更新發生時,對 Redis 的性能有比較大的影響。
雖然我們都知道這是極小的概率,但是這種設計缺陷卻不能被 Redis 的大佬作者所接受,因此在 5.0 版本中新引入了一個數據結構,名叫 listpack, 大家都將它翻譯爲 緊湊列表.
它的定義和 ziplist 極其相似,只是通過一些新的設計,來解決 ziplist 中的痛點問題。因此本文的講解基於讀者已經瞭解 ziplist.
ziplist的定義如下:
注意:這是 ziplist 的定義
struct ziplist<T>{
// 整個壓縮列表佔用字節數
int32 zlbytes;
// 最後一個節點到壓縮列表起始位置的偏移量,可以用來快速的定位到壓縮列表中的最後一個元素
int32 zltail_offset;
// 壓縮列表包含的元素個數
int16 zllength;
// 元素內容列表,用數組存儲,內存上緊挨着
T[] entries;
// 壓縮列表的結束標誌位,值永遠爲 0xFF.
int8 zlend;
}
listpack 的定義和上方基本一致,只是去掉了 zltail_offset 屬性。
讓我們回想一下,ziplist 中用這個屬性做什麼?用來方便的找到最後一個節點,然後方便進行反向的遍歷。新的 listpack 當然是解決了這個問題,才能放心的刪除掉這個屬性。
listpack節點的定義如下:
int<var> encoding;
optional byte[] content;
int<var> length;
相比於 ziplist 的定義,它有兩點改動:
- 記錄的長度不再是前一個節點的長度,而是自己的長度。
- 將記錄自己的長度放到了節點的尾部。
這樣做的好處是:
- 不再需要 zltail_offset 屬性也可以快速定位到最後一個節點。用
listpac 的總長度-最後一個節點的長度
. - 每個節點記錄自己的長度,當本節點的值發生了改變,只需要更改自己的長度即可。不再需要更改別的節點的屬性,也就徹底的解決掉了級聯更新問題。
總結
listpack 是 Redis 設計用來取代掉 ziplist 的數據結構,它通過每個節點記錄自己的長度,且放在節點的尾部,來徹底解決掉了 ziplist 存在的級聯更新的問題。
listpack 在 5.0 版本引入,但是由於 ziplist 在 Reids 內部的使用太過於廣泛,有一些兼容問題,我們可以預見這將是一個逐步的替換過程。
同樣在 5.0 版本引入的 Stream 數據結構中,就使用了 listpack 而不是 ziplist.
參考文章
《Redis 的設計與實現(第二版)》
《Redis 深度歷險:核心原理和應用實踐》
完。
聯繫我
最後,歡迎關注我的個人公衆號【 呼延十 】,會不定期更新很多後端工程師的學習筆記。
也歡迎直接公衆號私信或者郵箱聯繫我,一定知無不言,言無不盡。
以上皆爲個人所思所得,如有錯誤歡迎評論區指正。
歡迎轉載,煩請署名並保留原文鏈接。
聯繫郵箱:[email protected]
更多學習筆記見個人博客或關注微信公衆號 < 呼延十 >------>呼延十