Redis (二)擴容機制


 字典擴容需要同時滿足如下兩個條件:

  1、哈希表中保存的key數量超過了哈希表的大小(可以看出size既是哈希表大小,同時也是擴容閾值)

  2、當前沒有子進程在執行aof文件重寫或者生成RDB文件;或者保存的節點數與哈希表大小的比例超過了安全閾值(默認值為5)  

  redis 漸進式擴容機制 : https://blog.csdn.net/belalds/article/details/93713491?utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2~all~sobaiduend~default-1-93713491.nonecase&utm_term=redis%E5%AD%97%E5%85%B8%E6%89%A9%E5%AE%B9%E7%9A%84%E6%9D%A1%E4%BB%B6&spm=1000.2123.3001.4430

 

一、rehash

  字典初始化,在redis中字典中的hash表也是采用延遲初始化策略,在創建字典的時候並沒有為哈希表分配內存,只有當第一次插入數據時,才真正分配內存。看看字典創建函數dictCreate

  隨着操作的不斷進行,哈希表保存的鍵值對會逐漸增多或減少,為了讓哈希表負載因子維持在一個合理范圍之內,當哈希表保存的鍵值對太多或太少時,程序要對哈希表的大小進行相應的擴展或收縮。

  Redis對字典的哈希表執行rehash的步驟如下:

1、為字典的ht[1]哈希表分配空間,這個空間大小取決於要執行的操作:

  如果執行的是擴展操作,則ht[1]的大小為第一個大於等於等於ht[0].used*2的2^n;

  rehash后新生成的dictEntry節點數組大小等於超過當前key個數向上求整的2的n次方,比如當前key個數為100,則新生成的節點數組大小就是128

2、如果執行的收縮操作,則ht[1]的大小為第一個大於等於ht[0].used的2^n;

  將保存在ht[0]中的所有鍵值對rehash到ht[1]上面:rehash指的是重新計算鍵的哈希值和索引值,然后將鍵值對放置到ht[1]的指定位置上。

3、當ht[0]包含的所有鍵值對都遷移到ht[1]之后,釋放ht[0],將ht[1]設置為ht[0],並在ht[1]新創建一個空白哈希表,為下一次rehash做准備。

下面為一個rehash的實例 :

         

 

 

  2 * 4 = 8 ( 2 ^ 3 ) :

      

 

  重新計算索引,並復制, h [0] 所有的鍵值都遷移到 h [1]

      

 

  完成 rehash 之后的字典

      

 

 

 

哈希表的擴展與收縮

  redis中,每次插入鍵值對時,都會檢查是否需要擴容。如果滿足擴容條件,則進行擴容。

  當以下條件中任意一個被滿足時,程序會自動開始對哈希表執行擴展操作:

  1、服務器目前沒有執行BGSAVE或BGREWRITEAOF命令,並且哈希表負載因子大於等於1。

  2、服務器正在執行BGSAVE或BGREWRITEAOF命令,並且哈希表負載因子大於等於5。

  區分這兩種情況的目的在於,因為執行BGSAVE與BGREWRITEAOF過程中,Redis都需要創建子進程,而大多數操作系統都采用寫時復制技術來優化子進程使用效率,所以在子進程存在期間,服務器會提高執行擴展操作所需的負載因子,

從而盡可能避免在子進程存在期間進行哈希表擴展操作,這可以避免不必要的內存寫入,最大限度的節約空間。

  另一方面,當哈希表負載因子小於0.1時,程序自動開始對哈希表執行收縮操作。

 

二、漸進式rehash

  Redis中的rehash動作並不是一次性、集中式完成的,而是分多次、漸進式的完成的。

  這樣做的目的是,如果服務器中包含很多鍵值對,要一次性的將這些鍵值對全部rehash到ht[1]的話,龐大的計算量可能導致服務器在一段時間內停止服務於。

  為了避免這種影響,Redis采用了漸進式Redis:

  1、為ht[1]分配空間,讓字典同時持有ht[0]和ht[1]兩個哈希表。

  2、在字典中維持一個索引計數器變量rehashidx,並將它置為0,表示rehash工作開始。

  3、在rehash進行期間,每次對字典執行添加、刪除、查找或者更新操作時,程序除了執行指定操作以外,還會順帶將ht[0]哈希表在rehashidx索引上的所有鍵值對rehash到ht[1]中,當rehash工作完成之后,程序將rehashidx屬性的值+1。

  4、隨着字典操作的不斷進行,最終在某個時間點上,ht[0]的所有鍵值對都被rehash到ht[1]上,這時將rehashidx屬性設為-1,表示rehash完成。

  漸進式rehash 的好處在於其采取分而治之的方式,將rehash鍵值對所需要的計算工作均攤到字典的每個添加、刪除、查找和更新操作上,從而避免了集中式rehash而帶來的龐大計算量。

  下面為一個漸進式rehash的實例:

   准備開始 rehash

      

 

  rehash 索引 0 上的鍵值對

      

  rehash 索引 1 上的鍵值對

 

 

   rehash 索引 2 上的鍵值對

  

 

   rehash 索引 3 上的鍵值對

    

 

     rehash 執行完畢

    

 

   

漸進式rehash執行期間的哈希表操作

  因為在漸進式rehash的過程中,字典會同時使用ht[0]和ht[1]兩個哈希表,所以在漸進式rehash進行期間,字典的刪除、查找、更新等操作都是在兩個表上進行的。

  例如,查找操作會先在ht[0]上進行,如果沒找到再在ht[1]上進行。

  添加操作的鍵值對會一律保存到ht[1]中,這一措施保證ht[0]包含的鍵值對只會減少不會增加。

 

rehash后新生成的dictEntry節點數組大小等於超過當前key個數向上求整的2的n次方,比如當前key個數為100,則新生成的節點數組大小就是128


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM