Redis原始碼剖析(八)鍊錶

2021-09-26 13:58:29 字數 3021 閱讀 6448

在之前對redis的介紹中,可以看到鍊錶的使用頻率非常高。

鍊錶可以作為單獨的儲存結構,比如客戶端的監視鍊錶記錄該客戶端監視的所有鍵,伺服器的模式訂閱鍊錶記錄所有客戶端和它的模式訂閱。

鍊錶也可以內嵌到字典中作為字典的值型別,比如資料庫的監視字典使用鍊錶儲存監視某個鍵的所有客戶端,伺服器的訂閱字典使用鍊錶儲存訂閱某個頻道的所有客戶端。

redis中的鍊錶是雙向鍊錶,即每乙個節點都儲存了它的前驅節點和後繼節點,用於提高操作效率。節點定義如下

/* 鍊錶節點 */

typedef struct listnode listnode;

鍊錶結構主要記錄了表頭節點和表尾節點,節點個數以及一些函式指標,定義如下

/* 鍊錶 */

typedef struct list list;

函式指標主要是對節點值的操作,包括複製,析構,判斷是否相等

此外,redis還為鍊錶提供迭代器的功能,主要是對鍊錶節點的封裝,另外通過鍊錶節點的前驅節點和後繼節點,可以輕鬆的完成向前移動和向後移動

/* 迭代器 */

typedef struct listiter listiter;

direction的值有兩個,向前和向後,由巨集定義指出

#define al_start_head 0 /* 從頭到尾(向後) */

#define al_start_tail 1 /* 從尾到頭(向前) */

鍊錶的建立工作由listcreate函式完成,實際上就是申請鍊錶記憶體然後初始化成員變數

/* 建立乙個空鍊錶 */

list *listcreate(void)

刪除乙個鍊錶比建立稍微麻煩一點,因為需要釋放每個節點中儲存的值,沒錯,它正是呼叫free函式完成的

/* 釋放鍊錶的記憶體空間 */

void listrelease(list *list)

/* 因為list* 也是動態申請的,所以也需要釋放 */

zfree(list);

}

在其他模組的實現上,經常會看到向鍊錶尾部新增節點的操作,它的實現由listaddnodetail完成。函式首先為新節點申請記憶體,然後將節點新增到鍊錶中,這裡需要根據鍊錶之前是否為空執行不同操作

/* 在鍊錶尾部新增節點 */

list *listaddnodetail(list *list, void *value)

else

/* 節點個數加一 */

list->len++;

return list;

}

迭代器主要用於遍歷鍊錶,而迭代器的重點在移動上,通過direction變數,可以得知迭代器移動的方向,又通過鍊錶節點的前驅後繼節點,可以輕鬆實現移動操作

/* 移動迭代器,同時返回下乙個節點 */

listnode *listnext(listiter *iter)

/* 返回之前迭代器指向的節點 */

return current;

}

此外,redis提供了重置迭代器的操作,分別由listrewind和listrewindtail函式完成

/* 重置迭代器方向為從頭到尾,使迭代器指向頭節點 */

void listrewind(list *list, listiter *li)

/* 重置迭代器方向為從尾到頭,使迭代器指向尾節點 */

void listrewindtail(list *list, listiter *li)

有了迭代器的基礎,就可以實現鍊錶搜尋功能,即在鍊錶中查詢與某個值匹配的節點,需要利用迭代器遍歷鍊錶

/* 查詢值key,返回鍊錶節點 */

listnode *listsearchkey(list *list, void *key)

} else }}

return null;

}

除了上面提到的函式外,redis還提供了一些巨集定義函式,比如返回節點值,返回節點的前驅後繼節點等

/* 返回鍊錶節點個數 */

#define listlength(l) ((l)->len)

/* 返回頭節點 */

#define listfirst(l) ((l)->head)

/* 返回尾節點 */

#define listlast(l) ((l)->tail)

/* 返回前驅節點 */

#define listprevnode(n) ((n)->prev)

/* 返回後繼節點 */

#define listnextnode(n) ((n)->next)

/* 返回節點值 */

#define listnodevalue(n) ((n)->value)

/* 設定鍊錶的值複製,值析構,值匹配函式 */

#define listsetdupmethod(l,m) ((l)->dup = (m))

#define listsetfreemethod(l,m) ((l)->free = (m))

#define listsetmatchmethod(l,m) ((l)->match = (m))

/* 獲取鍊錶的值賦值,值析構,值匹配函式 */

#define listgetdupmethod(l) ((l)->dup)

#define listgetfree(l) ((l)->free)

#define listgetmatchmethod(l) ((l)->match)

由於鍊錶結構簡單,所以在實現上還是非常容易理解的。當然redis中與鍊錶有關的函式還有很多很多,這裡僅僅介紹了一些常用操作,有興趣可以深入原始碼檢視

Redis原始碼剖析 雙端鍊錶Sdlist

redis為雙端鍊錶的每乙個節點定義了如下的結構體。鍊錶節點定義 typedef struct listnode listnode 與一般的雙端鍊錶無異,定義了鍊錶節點的結構體之後,下面就定義鍊錶的結構體,用來方便管理鍊錶節點,其結構體定義如下 typedef struct list list re...

redis原始碼剖析 skiplist

試想乙個業務場景 遊戲需要實現乙個實時更新的排行榜應該如何實現 首先想到使用有序的雙端鍊錶,因為插入的時間複雜度為o 1 但是定位的平均時間複雜度為o n 當頻繁更新排行榜的時候效率比較低 有沒有乙個結構 能夠滿足快速定位到相應的位置並插入?跳躍表就能滿足這個需求 跳躍表的思想是給資料結點建立索引 ...

redis原始碼剖析 dict

typedef struct dictentry v struct dictentry next dictentry typedef struct dicttype dicttype this is our hash table structure.every dictionary has two ...