Redis原始碼分析(adlist)

2021-08-10 12:49:28 字數 4472 閱讀 7351

原始碼版本:redis-4.0.1

原始碼位置:

redis中的鍊錶叫adlist(a generic doubly linked list implementation 乙個通用的雙端鍊錶實現),和普通單鏈表相比,它的方向可以向前或者向後,這是由於資料結構中定義了nextprev兩個指標決定的,下面看下它的資料結構實現。

typedef

struct listnode listnode;

typedef

struct

list list;

老規矩,我們還是以乙個例子來分析原始碼,這個例子中會設計到adlist的建立、頭插、查詢、反轉輸出、複製、拼接這些操作,例子的**如下所示:

int keymatch(void *ptr, void *key) 

void printlist(list *li)

printf("\n");

}int main(int argc, char **argv)

; listiter iter;

listnode *node;

list *li = listcreate();

for (int i = 0; i < sizeof(b)/sizeof(*b); ++i)

printlist(li);

printf("\nsearch a key :\n");

listsetmatchmethod(li, keymatch);

listnode *ln = listsearchkey(li, "believe");

if (ln != null) else

printf("\nreverse output the list :\n");

printf("li size is %d, elements:", listlength(li));

listrewindtail(li, &iter);

while ((node = listnext(&iter)) != null)

printf("\n");

printf("\nduplicate a new list :\n");

list *lidup = listdup(li);

printlist(lidup);

printf("\nconnect two linked lists :\n");

listjoin(li, lidup);

printlist(li);

listrelease(li);

return0;}

out >

li size is 4, elements:not

or it believe

search a key :

find key is :believe

reverse output the list :

li size is 4, elements:believe it or

not

duplicate a new list :

li size is 4, elements:not

or it believe

connect two linked lists :

li size is 8, elements:not

or it believe not

or it believe

list *li = listcreate();建立了乙個list,並且返回了指標,**如下所示:

list

*listcreate(void)

listaddnodehead(li, b[i]);然後將預先定義好的陣列元素依次頭插入了list,與之對應的還有乙個尾插的函式listaddnodetail(),我們先看下頭插listaddnodehead()的**:

list

*listaddnodetail(list

*list, void

*value)

else

list

->len++;

return

list;

}

函式首先申請了乙個listnode節點,然後用list->len == 0判斷了是不是首節點,然後根據不同的策略交換指標,將元素頭插入鍊錶,將長度增加,迴圈插入所有元素之後鍊錶目前情況如下圖所示:

listnode *ln = listsearchkey(li, "believe");可以查詢第二個引數指定的字串,預設的匹配原則是比較指標是否相等,但是可以自定義match函式,因為我們的例子中需要比較字串,我自定義了keymatch函式如下所示:

match函式的宣告是:int (*match)(void *ptr, void *key); 

int keymatch(void *ptr, void *key)

listsetmatchmethod(li, keymatch);可以指定match函式,下面我們看下listsearchkey()函式的實現:

listnode *listsearchkey(list

*list, void

*key)

} else }}

因為adlist是雙端鍊錶,所以翻轉操作十分簡單,我們直接將迭代器初始化成從鍊錶尾部開始遍歷就完成了翻轉操作。

listrewindtail(li, &iter);    //將迭代器從尾部迭代
list *lidup = listdup(li);會建立一條新的鍊錶返回給使用者,但是需要注意預設的複製策略,如果使用者不自定義dup()函式,預設返回的複製鍊錶和原始鍊錶共用相同的資料節點,這樣對於乙個節點修改會導致原始鍊錶發生變化。如下所示:

list *lidup = listdup(li);                      //使用預設的複製操作

strncpy(listindex(lidup, 0)->value, "abc", 3); //修改複製返回的鍊錶的值

printlist(lidup);

printlist(li);

out >

li size is 4, elements:abc or it believe

li size is 4, elements:abc or it believe //可以看到原始鍊錶也受了影響,not 修改為了 abc

但是如果自定義dup函式,不再使得複製之後的鍊錶和原始鍊錶公用節點就可以避免這個問題:

void *strdup(void *ptr) 

listsetdupmethod(li, strdup); //設定自定義的dup函式

list *lidup = listdup(li);

strncpy(listindex(lidup, 0)->value, "abc", 3);

printlist

(lidup);

printlist

(li);

out >

lisize

is 4, elements:abc

orit

believe

lisize

is 4, elements:not

orit

believe //還是原始的值沒有變化

listjoin(li, lidup);可以將兩個鍊錶做連線操作:

void listjoin(list

*l, list

*o)

listrelease(li);函式負責釋放鍊錶,首先會呼叫listempty()函式釋放掉所有listnode,最後再釋放掉list本身的空間。

adlist的實現相對來說較簡單,我們上面分析了它的建立、插入、查詢、反轉等操作,基本上熟悉了api和底層資料結構的原理,但是由於鍊錶新增節點時候(無論頭插尾插)每次都是申請新的空間,所以比較容易造成記憶體碎片。這方面想想有無辦法優化。

[完]

Redis原始碼分析 intset h c

intset.h c 是redis 的整數set實現,intset的結構體如下 基本結構 typedef struct intset intset intset的第乙個成員encoding,表明contents中的儲存資料的資料長度,可以是16bits,32bits,64bits。第二個成員leng...

Redis原始碼分析系列

redis目前熱門nosql記憶體資料庫,量不是很大,本系列是本人閱讀redis原始碼時記錄的筆記,由於時間倉促和水平有限,文中難免會有錯誤之處,歡迎讀者指出,共同學習進步,本文使用的redis版本是2.8.19。redis之hash資料結構 redis之intset資料結構 redis之skipl...

redis原始碼分析 adlist

typedef struct listnode listnode 首先定義了乙個節點,包含前驅和後繼以及對應的value typedef struct listiter listiter list的迭代器,next指標和迭代方向 typedef struct list list 鍊錶內容 head和...