Redis 數據結構——鏈表

鏈表

鏈表在Redis中的應用非常廣泛,比如列表鍵的底層實現之一就是鏈表。當一個列表鍵包含了數量比較多的元素,又或者列表中包含的元素都是比較長的字符串時,Redis就會使用鏈表作爲列表鍵的底層實現。

除了鏈表鍵之外, 發佈與訂閱、慢查詢、監視器等功能也用到了鏈表, Redis 服務器本身還使用鏈表來保存多個客戶端的狀態信息, 以及使用鏈表來構建客戶端輸出緩衝區(output buffer)。

每個鏈表節點使用一個 adlist.h/listNode 結構來表示:

typedef struct listNode {
    // 前置節點
    struct listNode *prev;
    // 後置節點
    struct listNode *next;
    // 節點的值
    void *value;
} listNode;

使用 adlist.h/list 來持有鏈表的話, 操作起來會更方便:

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

list 結構爲鏈表提供了表頭指針 head 、表尾指針 tail , 以及鏈表長度計數器 len , 而 dupfreematch 成員則是用於實現多態鏈表所需的類型特定函數:

  • dup 函數用於複製鏈表節點所保存的值;

  • free 函數用於釋放鏈表節點所保存的值;

  • match 函數則用於對比鏈表節點所保存的值和另一個輸入值是否相等。

 

特性總結

  • 雙端: 鏈表節點帶有 prevnext 指針, 獲取某個節點的前置節點和後置節點的複雜度都是 O(1)

  • 無環: 表頭節點的 prev 指針和表尾節點的 next 指針都指向 NULL , 對鏈表的訪問以 NULL 爲終點。

  • 帶表頭指針和表尾指針: 通過 list 結構的 head 指針和 tail 指針, 程序獲取鏈表的表頭節點和表尾節點的複雜度爲 O(1)

  • 帶鏈表長度計數器: 程序使用 list 結構的 len 屬性來對 list 持有的鏈表節點進行計數, 程序獲取鏈表中節點數量的複雜度爲 O(1)

  • 多態: 鏈表節點使用 void* 指針來保存節點值, 並且可以通過 list 結構的 dupfreematch 三個屬性爲節點值設置類型特定函數, 所以鏈表可以用於保存各種不同類型的值。

鏈表和鏈表節點 API

函數 作用 時間複雜度
listSetDupMethod 將給定的函數設置爲鏈表的節點值複製函數。 O(1)
listGetDupMethod 返回鏈表當前正在使用的節點值複製函數。 複製函數可以通過鏈表的 dup 屬性直接獲得, O(1)
listSetFreeMethod 將給定的函數設置爲鏈表的節點值釋放函數。 O(1)
listGetFree 返回鏈表當前正在使用的節點值釋放函數。 釋放函數可以通過鏈表的 free 屬性直接獲得, O(1)
listSetMatchMethod 將給定的函數設置爲鏈表的節點值對比函數。 O(1)
listGetMatchMethod 返回鏈表當前正在使用的節點值對比函數。 對比函數可以通過鏈表的 match 屬性直接獲得,O(1)
listLength 返回鏈表的長度(包含了多少個節點)。 鏈表長度可以通過鏈表的 len 屬性直接獲得, O(1)
listFirst 返回鏈表的表頭節點。 表頭節點可以通過鏈表的 head 屬性直接獲得, O(1)
listLast 返回鏈表的表尾節點。 表尾節點可以通過鏈表的 tail 屬性直接獲得, O(1)
listPrevNode 返回給定節點的前置節點。 前置節點可以通過節點的 prev 屬性直接獲得, O(1)
listNextNode 返回給定節點的後置節點。 後置節點可以通過節點的 next 屬性直接獲得, O(1)
listNodeValue 返回給定節點目前正在保存的值。 節點值可以通過節點的 value 屬性直接獲得, O(1)
listCreate 創建一個不包含任何節點的新鏈表。 O(1)
listAddNodeHead 將一個包含給定值的新節點添加到給定鏈表的表頭。 O(1)
listAddNodeTail 將一個包含給定值的新節點添加到給定鏈表的表尾。 O(1)
listInsertNode 將一個包含給定值的新節點添加到給定節點的之前或者之後。 O(1)
listSearchKey 查找並返回鏈表中包含給定值的節點。 O(N)N 爲鏈表長度。
listIndex 返回鏈表在給定索引上的節點。 O(N)N 爲鏈表長度。
listDelNode 從鏈表中刪除給定節點。 O(1)
listRotate 將鏈表的表尾節點彈出,然後將被彈出的節點插入到鏈表的表頭, 成爲新的表頭節點。 O(1)
listDup 複製一個給定鏈表的副本。 O(N)N 爲鏈表長度。
listRelease 釋放給定鏈表,以及鏈表中的所有節點。 O(N)N 爲鏈表長度。

總結

  • 鏈表被廣泛用於實現 Redis 的各種功能, 比如列表鍵, 發佈與訂閱, 慢查詢, 監視器, 等等。

  • 每個鏈表節點由一個 listNode 結構來表示, 每個節點都有一個指向前置節點和後置節點的指針, 所以 Redis 的鏈表實現是雙端鏈表。

  • 每個鏈表使用一個 list 結構來表示, 這個結構帶有表頭節點指針、表尾節點指針、以及鏈表長度等信息。

  • 因爲鏈表表頭節點的前置節點和表尾節點的後置節點都指向 NULL , 所以 Redis 的鏈表實現是無環鏈表。

  • 通過爲鏈表設置不同的類型特定函數, Redis 的鏈表可以用於保存各種不同類型的值。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章