在上一章中咱們介紹了 String 的一些內部原理,在這一章中咱們再來討論在五種數據結構中 List 的基本使用和一些內部實現.java
Redis的List 呢至關於 Java 中的 LinkedList,也是雙向鏈表.具備一些和 LinkedList 一樣的特徵,好比插入和刪除一條很快,時間複雜度爲 O(1),獲取頭結點和尾節點也很快,時間複雜度也爲 O(1),隨機讀取則相對較慢時間複雜度爲 O(n).經常使用做消息隊列.node
當作隊列使用時,遵循先進先出原則:python
> rpush books python java golang (integer) 3 > lpop books "python" > lpop books "java"
當作棧使用時,遵循先進後出原則:golang
> rpush books python java golang (integer) 3 > rpop books "golang" > rpop books "java"
同時還能夠經過 get(index)的方法獲取:算法
> rpush books python java golang (integer) 3 > lindex books 0 "python" > lindex books -1 "golang"
index從 0 開始,能夠爲負數 -1 表明倒數第一個元素數據結構
上述部分咱們把 Redis 中的 List當作 Java 中的 LinkedList 操做,由於有不少相同的部分.但實際上在 Redis 中鏈表的內部實現可不是一個簡單的雙向鏈表.在數據量較少的時候它的底層存儲結構爲一塊連續內存,稱之爲ziplist
(壓縮列表).當數據量較多的時候將會變成鏈表的結構.後來由於鏈表須要 prev 和 next 兩個指針佔用內存不少,改用 ziplist+鏈表的混合結構,稱之爲 quicklist
(快速鏈表).在新的版本中 Redis 鏈表統一使用 quicklist來存儲.下面咱們就來詳細介紹這種數據結構.ui
先來看看 ziplist 的數據結構:編碼
struct ziplist<T>{ int32 zlbytes; //壓縮列表佔用字節數 int32 zltail_offset; //最後一個元素距離起始位置的偏移量,用於快速定位到最後一個節點 int16 zllength; //元素個數 T[] entries; //元素內容 int8 zlend; //結束位 0xFF }
如圖所示:spa
有了 ztail_offset 就能夠快速的定位到最後一個節點,這樣就能夠倒序遍歷了.也就是說 ziplist支持雙向遍歷.指針
下面再來看下 entry 的內部實現:
struct entry{ int<var> prevlen; //前一個 entry 的長度 int<var> encoding; //元素類型編碼 optional byte[] content; //元素內容 }
當 ziplist 倒序遍歷的時候,就是經過這個pervlen定位到前一個元素位置的.
encoding 保存了 content 的編碼類型.
content 則是保存的元素內容,它是optional 類型表示是這個字段是可選的.當content 是很小的整數時,他會內聯到 encoding 字段的尾部.
quicklist 是 ziplist 和鏈表的混合體.下面是 quicklist和 node 的部分數據結構:
struct quicklist{ quicklistNode* head; //指向頭結點 quicklistNode* tail; //指向尾節點 long count; //元素總數 int nodes; //quicklistNode節點的個數 int compressDepth; //壓縮算法深度 ... }
爲了節約空間 Redis 還會對 ziplist 使用 LZF 算法進行壓縮,能夠選擇壓縮深度.咱們待會在說.
如上圖所示,quicklist含有兩個 quicklistNode 表明頭結點和尾節點,其中每一個head 和 tail 之間是雙向鏈表.每一個quicklistNode指向一個 ziplist.
struct quicklistNode{ quicklistNode* prev; //前一個節點 quicklistNode* next; //後一個節點 ziplist* zl; //壓縮列表 int32 size; //ziplist大小 int16 count; //ziplist 中元素數量 int2 encoding; //編碼形式 存儲 ziplist 仍是進行 LZF 壓縮儲存 ... }
在 quicklist 中每一個 ziplist 默認大小是 8kb,超出這個字節就會增長一個 ziplist.這個默認大小是可配置的,由list-max-ziplist-size
決定.
上面說到 ziplist 可使用 LZF 算法壓縮,經過list-compress-depth
配置.默認狀況下quicklist 的壓縮深度是 0,也就是不壓縮.配置爲 1 的話表明從頭/尾開始第 1 個ziplsit 進行壓縮.
下章預告,Redis 數據結構之 Hash