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

前言

在上一章中咱們介紹了 String 的一些內部原理《你肯定不來了解下 Redis 字符串的原理嗎》,在這一章中咱們再來討論在五種數據結構中 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 表明倒數第一個元素bash

內部實現

上述部分咱們把 Redis 中的 List當作 Java 中的 LinkedList 操做,由於有不少相同的部分.但實際上在 Redis 中鏈表的內部實現可不是一個簡單的雙向鏈表.在數據量較少的時候它的底層存儲結構爲一塊連續內存,稱之爲ziplist(壓縮列表).當數據量較多的時候將會變成鏈表的結構.後來由於鏈表須要 prev 和 next 兩個指針佔用內存不少,改用 ziplist+鏈表的混合結構,稱之爲 quicklist(快速鏈表).在新的版本中 Redis 鏈表統一使用 quicklist來存儲.下面咱們就來詳細介紹這種數據結構.數據結構

ziplist 壓縮列表

先來看看 ziplist 的數據結構:post

struct ziplist<T>{
    int32 zlbytes;			//壓縮列表佔用字節數
    int32 zltail_offset;	//最後一個元素距離起始位置的偏移量,用於快速定位到最後一個節點
    int16 zllength;			//元素個數
    T[] entries;			//元素內容
    int8 zlend;				//結束位 0xFF
}
複製代碼

如圖所示:ui

有了 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

相關文章
相關標籤/搜索