概述
上篇博客我簡單介紹了 redis 字符串的實現原理。除了字符串外,鏈表作爲一種常用的數據結構,提供了高效的節點重排能力以及順序性的節點訪問。redis 使用的C語言沒有內置鏈表結構,本篇博客我就來整理下 redis 鏈表類型的實現。
redis 數據類型
redis 有以下五種常用的數據類型:
- String:字符串類型
- Hash:哈希類型
- list:鏈表類型
- set:集合類型
- zSet:有序集合類型
1、List 鏈表類型
C語言默認是不支持鏈表的,redis 使用頭文件 adlish.h 中的 ListNode 表示單個鏈表結構:
typedef struct listNode {
// 前置節點
struct listNode * prev;
// 後置節點
struct listNode * next;
// 節點的值
void * value;
}listNode;
多個節點之間通過 prev 和 next 指針組成雙端鏈表,具體結構如下所示:
僅僅使用 ListNode 是可以組成鏈表,但使用 adlish.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 結構爲鏈表提供了表頭、表尾、鏈表節點數以及三個不同類型的函數:
- dup 函數用於複製鏈表節點所保存的值;
- free 函數用於釋放鏈表節點所保存的值;
- match函數則用於對比鏈表節點所保存的值和另一個輸入值是否相等
dup、free,match 是用於實現多態鏈表所需的類型特定函數。下面我們看一張 List 和 ListNode 合用的結構圖:
總結一下,redis 鏈表結構的實現有以下特性:
- 雙向鏈接,獲取某個節點的前驅節點以及後記節點的時間複雜度都爲O(1)
- 無環,head 的前驅節點以及 tail 的後繼節點都爲 NULL
- 訪問鏈表頭節點和鏈表尾節點的時間複雜度都爲O(1)
- 訪問鏈表長度的時間複雜度爲O(1)
- 多態,鏈表節點使用 void* 指針來保存節點值,並可以通過 list 結構提供的 dup、free、match 三個屬性爲節點值設定特定類型函數,所以鏈表可以保存各種類型的值
鏈表常用 API
下面我通過表格的形式給出 redis 鏈表常用的函數方法:
鏈表用途
鏈表在 redis 中的用途非常廣泛。除了在 列表鍵 中使用外,很多其他功能也用到了鏈表。
- 鏈表鍵:當我們稱一個鍵爲列表鍵時,數據庫中該鍵鎖對應的值爲鏈表類型
redis 的發佈與訂閱、慢查詢、監視器等功能也用到了鏈表。redis 服務器本身還使用鏈表來保存多個客戶端的狀態信息,以及使用鏈表來構建客戶緩衝區。