《Redis 設計與實踐》讀書筆記系列五:字典 rehash

巴啦啦發表於2022-06-07

rehash

上一章節有提到,dictht是一個陣列,僅有兩個dictht,內容介紹了dictht的組成和關係。接下來的內容解釋了為什麼要有兩個dictht,原因可以總結為因為rehash

為什麼要有 rehash

隨著操作的不斷執行,雜湊表 ( dictht ) 中儲存的鍵值對會增多或減少,如果過多或過少,負載因子就會超出合理的範圍。為了讓雜湊表的負載因子維持在一個合理的範圍,就需要對雜湊表進行rehash,達到根據雜湊表的大小進行相應的擴充套件或收縮。

dict字典中有dictht屬性,是一個陣列,ht[1]則是用於rehash的。

未進行rehashdict的結構圖

rehash擴容的過程簡述

  1. ht[1]table新建一個dictEntry陣列(擴容必然大於ht[0]table陣列的容量)。
  2. ht[0]中的鍵值對重新以ht[1]中的屬性計算雜湊和索引值,對映到ht[1].table.dictEntry中的雜湊節點中,並將ht[0]dictEntry陣列索引指向的dictEntry設定為Null
  3. 釋放ht[0],並將ht[1]設定為ht[0],然後為ht[1]為配一個空的雜湊表(即table指向NULL)。

ht[1]的擴容演算法:

x = ht[0].used * 2
n = 2 的多少次方(while2的次方不斷累加)
n不斷的計算過程當中,當n第一次大於等於x時。n即為ht[1]的容量。例:

ht[0].used等於4時。x = 8n的冪不斷 + 1,導致n不斷變大,冪等於2時,n等於4n未大於等x。冪繼續+1,等於3n這時等於8,大於等於x。這時取8作為ht[1]size

ht[1]的收縮演算法:

同擴容演算法大概一致。不同的地方在於x的計算。收縮演算法中ht[0].used=x。即不用乘以2

擴容第二步結果圖

由上一圖作為 rehash 基礎作為演示:

rehash觸發條件

負載因子計算方式:load_factor = ht[0].used / ht[0].size

收縮觸發條件:負載因子小於0.1時。

擴容觸發條件:

  1. 當前沒有執行bgsavebgrewriteaof命令,且雜湊表負載因子>=1
  2. 當前正在執行bgsavebgrewriteaof命令,且雜湊表負載因子>=5

漸進式的rehash

rehash為什麼是漸進式的?

如果雜湊表的資料量很大,rehash的佔用會很高,涉及到雜湊計算和索引計算,資料轉移等。所以rehash需要分多次,漸進式的完成。

它是如何進行的?

它將rehash的操作平攤到每一次對該字典的增刪改查的操作上,即每次進行增刪改查,都會進行一部分的rehash操作,並將rehasidx修改。這就是漸進式,避免了集中式rehash帶來的龐大計算量。

上一圖可見,rehashidx值為3,表明了正在進行rehash。那為什麼是3而不是2rehashidx還代表著什麼?

rehashidx代表ht[0].table這個陣列中的索引,意味著當前rehash已經進行到了哪個索引了。3則意為著索引為3的鍵值對已轉移完畢。

當鍵值對全部轉移完畢後。rehashidx會重新歸於-1

當rehashidx不等於-1時,這個時候查詢一個鍵值對,會怎樣?

不等於-1,則意味著正在進行rehash,會先在ht[0]中查詢,沒找到就會在ht[1]中查詢。

當進行rehash時,新增一個鍵值對會怎樣?

會直接新增到ht[1]中。

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章