本筆記參考《Redis設(shè)計(jì)與實(shí)現(xiàn)》 P24~ 37
typedef struct dictEntry { // 鍵 void *key; // 值 : 可以是一個(gè)指針,或者是一個(gè)uint64/int64 的整數(shù) union { void *val; uint64_t u64; int64_t s64 } v; // 指向下一個(gè)哈希表節(jié)點(diǎn),形成鏈表 : 該指針可以將多個(gè)哈希值相同的鍵值對(duì)連接在一起,以此解決鍵沖突的問題。 struct dictEntry *next; } dictEntry;
typedef struct dictht { // 哈希表數(shù)據(jù) dictEntry **table; // 哈希表集合大小 unsigned long size; // 哈希表大小掩碼,用于計(jì)算索引值 // 總是等于 size - 1 unsigned long sizemask; // 哈希表已有節(jié)點(diǎn)數(shù)量 unsigned long used; } dictht;
typedef struct dict { // 類型特定函數(shù) dicType *type; // 私有數(shù)據(jù) void *privdata; // 哈希表 dictht ht[2]; // rehash 索引 // 當(dāng)rehash不在進(jìn)行時(shí), 值為-1 int rehashidx; } dict;
type屬性和privdata屬性針對(duì)不同類型的鍵值對(duì),為多態(tài)字典而設(shè)置。
ht是包含兩個(gè)項(xiàng)的數(shù)組,每個(gè)元素都是一個(gè)dictht哈希表,一般情況下字典之是喲個(gè)ht[0],ht[1]會(huì)在對(duì)ht[0]進(jìn)行rehash的時(shí)候使用。
rehashidx記錄了rehash目前的進(jìn)度,如果目前沒有在進(jìn)行rehash,值為-1。
hash = dict->type->hashFunction(key);
index = hash dict->ht[x].sizemask;
redis使用的是MurmurHash算法,優(yōu)點(diǎn)是:輸入的鍵是有規(guī)律的時(shí)候,算法仍然能給出很好的隨機(jī)分布性,計(jì)算速度也快。
當(dāng)有兩個(gè)或以上的key分配到了hash table數(shù)組的同一個(gè)index上,稱為發(fā)生了collision。
Redis采用鏈地址法解決沖突,每個(gè)hash table節(jié)點(diǎn)都有一個(gè)next指針,多個(gè)hash table節(jié)點(diǎn)可以用next指針構(gòu)成一個(gè)單向鏈表。為了速度考慮,程序總是會(huì)將新節(jié)點(diǎn)插入到鏈表頭位置。
隨著操作不斷執(zhí)行,哈希表保存的key value對(duì)會(huì)逐漸增加和減少。哈希表有一個(gè)統(tǒng)計(jì)參數(shù)load factor,即負(fù)載因子,公式如下:
# 負(fù)載因子 = 哈希表已經(jīng)保存的節(jié)點(diǎn)數(shù)量 / 哈希表大小 load_factor = ht[0].used / ht[0].size;
為了維持負(fù)載因子在一個(gè)合理的范圍,程序會(huì)對(duì)哈希表的大小進(jìn)行相應(yīng)的擴(kuò)展或收縮,條件如下:
1、服務(wù)器目前沒有執(zhí)行BGSAVE命令或者BGREWRITEAOF命令,并且哈希表的負(fù)載因子 >= 1
2、服務(wù)器正在執(zhí)行BGSAVE命令或者BGREWRITEAOF命令,且負(fù)載因子 >= 5
rehash的動(dòng)作并不是一次性集中完成的,而是分多次漸進(jìn)完成。
如果哈希表中村的鍵值對(duì)數(shù)量很多,一次性將鍵值對(duì)全部rehash到ht[1]的計(jì)算量十分龐大,可能會(huì)導(dǎo)致服務(wù)器在一段時(shí)間內(nèi)停止服務(wù)。
漸進(jìn)式rehash采取分而治之的方法,將rehash鍵值對(duì)所需要的計(jì)算工作分?jǐn)偟矫看螌?duì)字典的CRUD操作上,從而避免了集中式rehash帶來的龐大計(jì)算量。
詳細(xì)步驟如下:
1、為ht[1]分配空間,讓字典同時(shí)持有ht[0]和ht[1]兩個(gè)哈希表
2、在字典中維護(hù)一個(gè)索引計(jì)數(shù)器:rehashidx,將值設(shè)置為0,表示rehash工作正式開始。
3、在rehash進(jìn)行期間,每次對(duì)字典的CRUD操作,程序除了執(zhí)行指定操作以外,順帶將ht[0]哈希表在rehashidx索引上的所有鍵值對(duì)rehash到ht[1]上,當(dāng)rehash操作完成后,程序?qū)ehashidx值++
4、重復(fù)迭代操作執(zhí)行后,ht[0]的數(shù)據(jù)全部rehash到ht[1]上,將rehashidx設(shè)為-1,表明rehash操作已經(jīng)完成
需要注意的地方
在rehash的過程中,對(duì)于字典的刪除、查找、更新操作會(huì)在兩個(gè)哈希表上執(zhí)行。如想要查找一個(gè)鍵,現(xiàn)在ht[0]中找,沒有找到再去ht[1]
對(duì)于insert操作來說,新添加到字典的鍵值對(duì)會(huì)一律保存到ht[1]中,不然還得多一次搬運(yùn)。
到此這篇關(guān)于Redis字典實(shí)現(xiàn)、Hash鍵沖突以及漸進(jìn)式rehash的文章就介紹到這了,更多相關(guān)Redis 漸進(jìn)式rehash內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
標(biāo)簽:北京 吉安 臺(tái)州 朝陽 江蘇 楊凌 果洛 大慶
巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《Redis字典實(shí)現(xiàn)、Hash鍵沖突及漸進(jìn)式rehash詳解》,本文關(guān)鍵詞 Redis,字典,實(shí)現(xiàn),Hash,鍵,沖突,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無關(guān)。