Redis原始碼閱讀 dict字典的實現

2022-01-11 01:18:29 字數 2341 閱讀 7977

dict是一種用於儲存鍵值對的抽象資料結構,在redis中使用非常廣泛,比如資料庫、雜湊結構的底層。

當執行下面這個命令:

> set msg "hello"
以及使用雜湊結構,如:

> hset people name "hoohack"
都會使用到dict作為底層資料結構的實現。

/* 字典結構 每個字典有兩個雜湊表,實現漸進式雜湊時需要用在將舊表rehash到新錶 */

typedef struct dict dict;

/* 雜湊表結構 */

typedef struct dictht dictht;

/* 雜湊表節點 */

typedef struct dictentry v; /* 值 */

struct dictentry *next; /* 指向下乙個節點, 將多個雜湊值相同的鍵值對連線起來*/

} dictentry;

/* 儲存一連串操作特定型別鍵值對的函式 */

typedef struct dicttype dicttype;

把上面的結構定義串起來,得到下面的字典資料結構:

根據資料結構定義,把關聯圖畫出來後,看**的時候就更加清晰。

從圖中也可以看出來,字典的雜湊表裡,使用了鍊錶解決鍵衝突的情況,稱為鏈式位址法。

當操作越來越多,比如不斷的向雜湊表新增元素,此時雜湊表需要分配了更多的空間,如果接下來的操作是不斷地刪除雜湊表的元素,那麼雜湊表的大小就會發生變化,更重要的是,現在的雜湊表不再需要那麼大的空間了,在redis的實現中,為了保證雜湊表的負載因子維持在乙個合理範圍內,當雜湊表儲存的鍵值對太多或者太少時,redis對雜湊表大小進行相應的擴充套件和收縮,稱為rehash(重新雜湊)。

負載因子 = 雜湊表已儲存節點數量 / 雜湊表大小

負載因子越大,意味著雜湊表越滿,越容易導致衝突,效能也就越低。因此,一般來說,當負載因子大於某個常數(可能是 1,或者 0.75 等)時,雜湊表將自動擴容。

在上面的rehash流程圖裡面,rehash的操作不是一次性就完成了的,而是分多次,漸進式地完成。

原因是,如果需要rehash的鍵值對較多,會對伺服器造成效能影響,漸進式地rehash避免了對伺服器的影響。

漸進式的rehash使用了dict結構體中的rehashidx屬性輔助完成。當漸進式雜湊開始時,rehashidx會被設定為0,表示從dictentry[0]開始進行rehash,每完成一次,就將rehashidx加1。直到ht[0]中的所有節點都被rehash到ht[1],rehashidx被設定為-1,此時表示rehash結束。

/* 實現漸進式的重新雜湊,如果還有需要重新雜湊的key,返回1,否則返回0

* * 需要注意的是,rehash持續將bucket從老的雜湊表移到新的雜湊表,但是,因為有的雜湊表是空的,

* 因此函式不能保證即使乙個bucket也會被rehash,因為函式最多一共會訪問n*10個空bucket,不然的話,函式將會耗費過多效能,而且函式會被阻塞一段時間

*/int dictrehash(dict *d, int n)

de = d->ht[0].table[d->rehashidx];

/* 實現將bucket從老的雜湊表移到新的雜湊表 */

while(de)

d->ht[0].table[d->rehashidx] = null;

d->rehashidx++;

}/* 如果已經完成了,釋放舊的雜湊表,返回0 */

if (d->ht[0].used == 0)

/* 繼續下一次rehash */

return 1;

}

在漸進式rehash期間,所有對字典的操作,包括:新增、查詢、更新等等,程式除了執行指定的操作之外,還會順帶將ht[0]雜湊表索引的所有鍵值對rehash到ht[1]。比如新增:

dictentry *dictaddraw(dict *d, void *key, dictentry **existing)

使用乙個標記值標記某項操作正在執行是程式設計中常用的手段,比如本文提到的rehashidx,多利用此手段可以解決很多問題。

我在github有對redis原始碼更詳細的註解。感興趣的可以圍觀一下,給個star。redis4.0原始碼註解。可以通過commit記錄檢視已新增的註解。

原創文章,文筆有限,才疏學淺,文中若有不正之處,萬望告知。

redis原始碼剖析 dict

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

redis原始碼之dict

大家都知道redis預設是16個db,但是這些db底層的設計結構是什麼樣的呢?我們來簡單的看一下原始碼,重要的字段都有所注釋 typedef struct redisdb redisdb redis中的所有kv都是存放在dict中的,dict型別在redis中非常重要。字典disc的資料結構如下 t...

redis原始碼閱讀筆記

在redis中乙個資料庫結構體是這樣的 每個dict是乙個hash表 typedef struct redisdb redisdb dict欄位中存放以key值為鍵,以value指標為值的hash表項dict根據型別的不同分為如下幾種 1 字串 string 操作 set key value get...