Redis是一個(gè)開源的內(nèi)存數(shù)據(jù)庫(kù),使用鍵值對(duì)存儲(chǔ)數(shù)據(jù)。其中,Redis中的數(shù)據(jù)結(jié)構(gòu)之一就是哈希(Hash),它提供了一種將多個(gè)字段(Field)存儲(chǔ)在一個(gè)鍵(Key)中的方法。那么Redis的哈希數(shù)據(jù)結(jié)構(gòu)是如何實(shí)現(xiàn)的呢?本文將詳細(xì)介紹Redis哈希底層的實(shí)現(xiàn)原理。
在Redis中,每個(gè)哈希都是由一個(gè)類似于字典(Dictionary)的結(jié)構(gòu)實(shí)現(xiàn)的,其中使用鏈地址法解決哈希沖突。整個(gè)哈希表的結(jié)構(gòu)如下所示:
typedef struct dict {
dictType *type;
void *privdata;
dictht ht[2];
int rehashidx; /* rehashing 進(jìn)度標(biāo)識(shí),當(dāng)進(jìn)行漸進(jìn)式rehash時(shí),這個(gè)值表示目前操作的(ht[0]或者h(yuǎn)t[1])桶的索引位置*/
int iterators; /* number of iterators currently running 哈希表上目前運(yùn)行的迭代器數(shù)量*/
} dict;
typedef struct dictht {
dictEntry **table; /* 哈希表數(shù)組,每個(gè)元素都是指向dictEntry結(jié)構(gòu)的指針(指針數(shù)組) */
unsigned long size; /* 哈希表大小,是桶數(shù),不能大于2^32 */
unsigned long sizemask; /* 哈希表大小掩碼,用于計(jì)算索引值,等于size-1,位運(yùn)算提高性能 */
unsigned long used; /* 哈希表已有節(jié)點(diǎn)數(shù)量 */
} dictht;
typedef struct dictEntry {
void *key;
union {
void *val;
uint64_t u64;
int64_t s64;
double d;
} v;
struct dictEntry *next; /* 鏈地址法解決沖突 */
} dictEntry;
上述代碼中的dict
結(jié)構(gòu)表示一個(gè)哈希表,其中ht[0]
表示當(dāng)前哈希表,ht[1]
表示進(jìn)行漸進(jìn)式rehash時(shí)的輔助哈希表(當(dāng)需要對(duì)哈希表進(jìn)行擴(kuò)容時(shí),會(huì)使用輔助哈希表提前申請(qǐng)新的內(nèi)存)。
而dictht
結(jié)構(gòu)表示哈希表內(nèi)部的哈希數(shù)組,table
是一個(gè)指針數(shù)組,將哈希桶中的元素以鏈表的形式進(jìn)行存儲(chǔ)。
對(duì)于每個(gè)哈希鍵值對(duì),Redis使用dictEntry
結(jié)構(gòu)來(lái)表示,其中key
是一個(gè)指向鍵的指針,v
是一個(gè)聯(lián)合體,可以存儲(chǔ)不同類型的值(Redis值對(duì)象),例如整型、浮點(diǎn)型、字符串等。
具體來(lái)說(shuō),Redis哈希底層實(shí)現(xiàn)的步驟如下:
- 根據(jù)鍵值對(duì)的鍵,通過(guò)哈希函數(shù)計(jì)算出哈希值。
- 根據(jù)哈希值計(jì)算出存儲(chǔ)位置(索引)。
- 在哈希表中找到對(duì)應(yīng)索引位置的哈希桶(鏈表),然后遍歷整個(gè)鏈表,查找是否有相同鍵的節(jié)點(diǎn)。
- 如果找到相同鍵的節(jié)點(diǎn),根據(jù)操作類型進(jìn)行更新或刪除操作。
- 如果沒有找到相同鍵的節(jié)點(diǎn),則創(chuàng)建新的節(jié)點(diǎn)并將其插入到鏈表中。
下面是哈希表的插入、查找、刪除的具體實(shí)現(xiàn)細(xì)節(jié):
- 插入:首先通過(guò)哈希函數(shù)將鍵轉(zhuǎn)換為哈希值,并計(jì)算出插入位置。然后,查詢?cè)撐恢脤?duì)應(yīng)的哈希桶,遍歷鏈表,查找是否已經(jīng)存在相同的鍵。如果存在相同鍵,則更新對(duì)應(yīng)節(jié)點(diǎn)的值。如果不存在相同鍵,則創(chuàng)建新的節(jié)點(diǎn)并將其插入到鏈表首部。如果鏈表長(zhǎng)度過(guò)長(zhǎng)(超過(guò)一定閾值),則將鏈表轉(zhuǎn)化為紅黑樹(時(shí)間復(fù)雜度由O(n)降低為O(log n))。
- 查找:通過(guò)哈希函數(shù)計(jì)算鍵對(duì)應(yīng)的哈希值,然后在哈希表中查找對(duì)應(yīng)索引的哈希桶。遍歷鏈表,查找是否存在相同鍵的節(jié)點(diǎn)。如果存在,則返回節(jié)點(diǎn)的值;如果不存在,則返回空指針。
- 刪除:通過(guò)哈希函數(shù)計(jì)算鍵對(duì)應(yīng)的哈希值,然后在哈希表中查找對(duì)應(yīng)索引的哈希桶。遍歷鏈表,查找是否存在相同鍵的節(jié)點(diǎn)。如果存在,則刪除該節(jié)點(diǎn),并釋放其內(nèi)存;如果不存在,則不進(jìn)行任何操作。
需要注意的是,在Redis中,哈希表的擴(kuò)容和縮容是通過(guò)漸進(jìn)式rehash來(lái)實(shí)現(xiàn)的。漸進(jìn)式rehash的過(guò)程分為兩個(gè)階段,首先,Redis會(huì)在擴(kuò)容時(shí)創(chuàng)建一個(gè)新的輔助哈希表(ht[1]),然后將原有哈希表(ht[0])中的節(jié)點(diǎn)逐個(gè)遷移到輔助哈希表中。在這個(gè)過(guò)程中,Redis會(huì)通過(guò)rehashidx來(lái)標(biāo)識(shí)當(dāng)前操作的桶的索引位置。當(dāng)遷移完成后,Redis會(huì)停止對(duì)ht[0]的操作,并釋放其內(nèi)存。
綜上所述,Redis的哈希底層實(shí)現(xiàn)主要是基于字典結(jié)構(gòu)和鏈地址法解決哈希沖突的思想。通過(guò)哈希函數(shù)計(jì)算鍵對(duì)應(yīng)的哈希值,并在哈希表中查找對(duì)應(yīng)的哈希桶。通過(guò)遍歷鏈表或者紅黑樹,實(shí)現(xiàn)插入、查找和刪除等操作。此外,Redis還使用漸進(jìn)式rehash來(lái)實(shí)現(xiàn)哈希表的擴(kuò)容和縮容。通過(guò)這些實(shí)現(xiàn),Redis的哈希數(shù)據(jù)結(jié)構(gòu)能夠高效地存儲(chǔ)和操作大量的鍵值對(duì)數(shù)據(jù)。
-
內(nèi)存
+關(guān)注
關(guān)注
8文章
2902瀏覽量
73534 -
數(shù)據(jù)庫(kù)
+關(guān)注
關(guān)注
7文章
3711瀏覽量
64023 -
Hash
+關(guān)注
關(guān)注
0文章
32瀏覽量
13160 -
Redis
+關(guān)注
關(guān)注
0文章
368瀏覽量
10780
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論