1. 程式人生 > 其它 >不是吧?為了加快速度,溫故而知新

不是吧?為了加快速度,溫故而知新

不是吧?為了加快速度,溫故而知新

列表物件

同字串物件一樣,列表物件到底使用哪一種資料結構來進行儲存也是通過編碼來進行區分:

編碼屬性描述object encoding命令返回值OBJ_ENCODING_LINKEDLIST使用 linkedlist 實現列表物件linkedlistOBJ_ENCODING_ZIPLIST使用 ziplist 實現列表物件ziplistOBJ_ENCODING_QUICKLIST使用 quicklist 實現列表物件quicklist

linkedlist

linkedlist 是一個雙向列表,每個節點都會儲存指向上一個節點和指向下一個節點的指標。linkedlist 因為每個節點之間的空間是不連續的,所以可能會造成過多的記憶體空間碎片。

  • linkedlist儲存結構

連結串列中每一個節點都是一個 listNode 物件(原始碼 adlist.h 內),不過需要注意的是,列表中的 value 其實也是一個字串物件,其他幾種資料型別其內部最終也是會巢狀字串物件,字串物件也是唯一一種會被其他物件引用的基本型別:

typedef struct listNode {
    struct listNode *prev;//前一個節點
    struct listNode *next;//後一個節點
    void *value;//值(字串物件)
} listNode;

然後會將其再進行封裝成為一個 list 物件(原始碼 adlist.h 內):

typedef struct list {
    listNode *head;//頭節點
    listNode *tail;//尾節點
    void *(*dup)(void *ptr);//節點值複製函式
    void (*free)(void *ptr);//節點值釋放函式
    int (*match)(void *ptr, void *key);//節點值對比函式
    unsigned long len;//節點數量
} list;

Redis 中對 linkedlist 的訪問是以 NULL 值為終點的,因為 head 節點的 prev 節點為 NULL,tail 節點的 next 節點也為 NULL,所以從頭節點開始遍歷,當發現 tail 為 NULL 時,則可以認為已經到了列表末尾。

當我們設定一個列表物件時,在 Redis 3.2 版本之前我們可以得到如下儲存示意圖:

ziplist

壓縮列表在前面已經介紹過,想要詳細瞭解的可以點選這裡。

linkedlist 和 ziplist 的選擇

在 Redis3.2 之前,linkedlist 和 ziplist 兩種編碼可以進選擇切換,如果需要列表使用 ziplist 編碼進行儲存,則必須滿足以下兩個條件:

  • 列表物件儲存的所有字串元素的長度都小於 64 位元組。
  • 列表物件儲存的元素數量小於 512 個。

一旦不滿足這兩個條件的任意一個,則會使用 linkedlist 編碼進行儲存。

PS:這兩個條件可以通過引數 list-max-ziplist-value 和 list-max-ziplist-entries 進行修改。

這兩種列表能在特定的場景下發揮各自的作用,應該來說已經能滿足大部分需求了,然後 Redis 並不滿足於此,於是一場改革引發了,quicklist 橫空出世。

quicklist

在 Redis 3.2 版本之後,為了進一步提升 Redis 的效能,列表物件統一採用 quicklist 來儲存列表物件。quicklist儲存了一個雙向列表,每個列表的節點是一個 ziplist,所以實際上 quicklist 並不是一個新的資料結構,它就是linkedlist 和 ziplist 的結合,然後被命名為快速列表。

  • quicklist 內部儲存結構

quicklist 中每一個節點都是一個 quicklistNode 物件,其資料結構定義如下:

typedef struct quicklistNode {
    struct quicklistNode *prev;//前一個節點
    struct quicklistNode *next;//後一個節點
    unsigned char *zl;//當前指向的ziplist或者quicklistLZF
    unsigned int sz;//當前ziplist佔用位元組
    unsigned int count : 16;//ziplist中儲存的元素個數,16位元組(最大65535個)
    unsigned int encoding : 2; //是否採用了LZF壓縮演算法壓縮節點 1:RAW 2:LZF
    unsigned int container : 2; //儲存結構,NONE=1, ZIPLIST=2
    unsigned int recompress : 1; //當前ziplist是否需要再次壓縮(如果前面被解壓過則為true,表示需要再次被壓縮)
    unsigned int attempted_compress : 1;//測試用 
    unsigned int extra : 10; //後期留用
} quicklistNode;

然後各個 quicklistNode 就構成了一個快速列表 quicklist:

typedef struct quicklist {
    quicklistNode *head;//列表頭節點
    quicklistNode *tail;//列表尾節點
    unsigned long count;//ziplist中一共儲存了多少元素,即:每一個quicklistNode內的count相加
    unsigned long len; //雙向連結串列的長度,即quicklistNode的數量
    int fill : 16;//填充因子
    unsigned int compress : 16;//壓縮深度 0-不壓縮
} quicklist;

根據這兩個結構,我們可以得到 Redis 3.2 版本之後的列表物件的一個儲存結構示意圖:

  • quicklist 的 compress 屬性

compress 是用來表示壓縮深度,ziplist 除了記憶體空間是連續之外,還可以採用特定的 LZF 壓縮演算法來將節點進行壓縮儲存,從而更進一步的節省空間,壓縮深度可以通過引數 list-compress-depth 控制:

  • 0:不壓縮(預設值)
  • 1:首尾第1個元素不壓縮
  • 2:首位前2個元素不壓縮

最後

我想問下大家當初選擇做程式設計師的初衷是什麼?有思考過這個問題嗎?高薪?熱愛?

既然入了這行就應該知道,這個行業是靠本事吃飯的,你想要拿高薪沒有問題,請好好磨練自己的技術,不要抱怨。有的人通過培訓可以讓自己成長,有些人可以通過自律強大的自學能力成長,如果你兩者都不佔,還怎麼拿高薪?

架構師是很多程式設計師的職業目標,一個好的架構師是不愁所謂的35歲高齡門檻的,到了那個時候,照樣大把的企業挖他。為什麼很多人想進阿里巴巴,無非不是福利待遇好以及優質的人脈資源,這對個人職業發展是有非常大幫助的。

如果你也想成為一名好的架構師,那或許這份Java核心架構筆記你需要閱讀閱讀,希望能夠對你的職業發展有所幫助。

中高階開發必知必會: