Redis設計與實現 -- 鏈表與字典

1. 鏈表

1.1 鏈表的結構

在 Redis 中,鏈表的實現是雙向鏈表,除此之外與常規的鏈表不同的是它還有三個函數指針,dup 函數用於複製鏈表節點所保存的值,free 函數用於釋放鏈表節點保存的值,match 函數則用於對比鏈表節點的值和另一個值是否相等。

1.2 總結

2. 字典

2.1 字典的定義

Redis 的字典與 C++ 的 map 類似,key 與 value 進行關聯,一個字典可以包含多個鍵值對。

2.2 哈希表的實現

Redis 的字典底層實現爲哈希表,一個哈希表有多個哈希節點,每個哈希節點保存着一個鍵值對。Redis 的哈希表結構定義如下。

其中 table 爲一個 dictEntry 數組,每個元素保存着一個鍵值對, dictEntry 的結構定義如下。

爲了防止哈希後鍵衝突,dictEntry 設計了一個 next 字段,形成了一個鏈表,鍵衝突後的結構如下圖所示:

2.3 字典

在 dict 結構體中 ht 字段是一個長度爲 2 的數組,每個元素都是一個 dictht 哈希表,默認使用 ht[0],ht[1] 在對 ht[0] 進行 rehash 的時候使用,trehashidx 表示 rehash 的進度,值爲 -1 的時候表示目前沒有在 rehash。普通狀態下的 dict (沒有 rehash)如下所示

2.4 哈希算法

2.5 鍵衝突

當有多個鍵被分配到哈希數組的同一索引上時,則產生了鍵衝突,Redis 解決該問題的方法是使用鏈地址法,即每個節點都有一個 next 指針, 多個哈希表的節點可以使用 next 指針連接起來,由於 dictEntry 節點組成的鏈表沒有指向尾部的指針,如果在尾部插入需要 O(N) 的時間複雜度,考慮到效率問題,總是將新節點插入在鏈表的頭部。

以上圖爲例,插入一個新的 dictEntry 節點,鍵值對爲 k2, v2,可以看出是插入到頭部的。

2.6 rehash

 當哈希表的鍵值對過多或者過少時,會對哈希表的大小進行相應的擴展或者收縮。rehash 的步驟如下:

哈希表的擴展收縮都是自動的,當以下條件滿足時自動開始對哈希表進行擴展動作:

  • 服務器目前沒有執行 BGSAVE 命令或者 BGREWRITEAOF 命令且哈希表的負載因子大於等於 1 。
  • 服務器目前正在執行 BGSAVE 命令或者 BGREWRITEAOF 命令且哈希表的負載因子大於等於 5 。

負載因子的計算公式爲:ht[0].used / ht[0].size 。如一個大小爲 512,包含 256 個鍵值對的哈希表來說, 其 負載因子爲 256 / 512 = 0.5,當負載因子小於 0.1 時,自動執行收縮操作。

2.7 漸進式 rehash

哈希表的擴展和收縮是將所有的鍵值對 rehash 到 ht[1] 中,而這個 rehash 的過程不是一次性將所有的鍵值對 rehash 過去,而是分多次漸進式的進行,原因在於當鍵值對過多,如數百萬個時,計算量過於龐大可能導致服務器在一段時間停止服務。這種漸進式的 rehash 步驟如下:

 

 

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