你肯定不來了解一下Redis列表的原理嗎

前言

在上一章中咱們介紹了 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 壓縮列表

先來看看 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 快速列表

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

相關文章
相關標籤/搜索