溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

Redis字典知識(shí)點(diǎn)有哪些

發(fā)布時(shí)間:2021-12-27 16:44:38 來(lái)源:億速云 閱讀:119 作者:iii 欄目:大數(shù)據(jù)

這篇文章主要講解了“Redis字典知識(shí)點(diǎn)有哪些”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“Redis字典知識(shí)點(diǎn)有哪些”吧!

字典數(shù)據(jù)結(jié)構(gòu)

說(shuō)起字典,也許大家比較陌生,但是我們都知道 Redis 本身提供 KV 查詢(xún)的方式,這個(gè) KV 就是其實(shí)通過(guò)底層就是通過(guò)字典保存。

另外,Redis 支持多種數(shù)據(jù)類(lèi)型,其中一種類(lèi)型為 Hash 鍵,也可以用來(lái)存儲(chǔ) KV 數(shù)據(jù)。

阿粉剛開(kāi)始了解的這個(gè)數(shù)據(jù)結(jié)構(gòu)的時(shí)候,本來(lái)以為這個(gè)就是使用字典實(shí)現(xiàn)。其實(shí)并不是這樣的,初始創(chuàng)建 Hash 鍵,默認(rèn)使用另外一種數(shù)據(jù)結(jié)構(gòu)-「ZIPLIST」(壓縮列表),以此節(jié)省內(nèi)存空間。

不過(guò)一旦以下任何條件被滿(mǎn)足,Hash 鍵的數(shù)據(jù)結(jié)構(gòu)將會(huì)變?yōu)樽值?,加快查?xún)速度。

  • 哈希表中某個(gè)鍵或某個(gè)值的長(zhǎng)度大于 server.hash_max_ziplist_value (默認(rèn)值為 64 )。
  • 壓縮列表中的節(jié)點(diǎn)數(shù)量大于 server.hash_max_ziplist_entries (默認(rèn)值為 512 )。

Redis 字典新建時(shí)默認(rèn)將會(huì)創(chuàng)建一個(gè)哈希表數(shù)組,保存兩個(gè)哈希表。

其中 ht[0] 哈希表在第一次往字典中添加鍵值時(shí)分配內(nèi)存空間,而另一個(gè) ht[1] 將會(huì)在下文中擴(kuò)容/縮容才會(huì)進(jìn)行空間分配。

Redis字典知識(shí)點(diǎn)有哪些

字典中哈希表其實(shí)就等同于Java HashMap,我們知道 Java 采用數(shù)組加鏈表/紅黑樹(shù)的實(shí)現(xiàn)方式,其實(shí)哈希表也是使用類(lèi)似的數(shù)據(jù)結(jié)構(gòu)。

哈希表結(jié)構(gòu)如下所示:

Redis字典知識(shí)點(diǎn)有哪些

其中 table 屬性是個(gè)數(shù)組, 其中數(shù)組元素保存一種 dictEntry 的結(jié)構(gòu),這個(gè)結(jié)構(gòu)完全類(lèi)似與 HashMap 中的  Entry 類(lèi)型,這個(gè)結(jié)構(gòu)存儲(chǔ)一個(gè) KV 鍵值對(duì)。

同時(shí),為了解決 hash 碰撞的問(wèn)題,dictEntry 存在一個(gè) next 指針,指向下一個(gè)dictEntry ,這樣就形成  dictEntry  的鏈表。

Redis字典知識(shí)點(diǎn)有哪些

現(xiàn)在,我們回頭對(duì)比 Java 中 HashMap,可以發(fā)現(xiàn)兩者數(shù)據(jù)結(jié)構(gòu)基本一致。

只不過(guò) HashMap 為了解決鏈表過(guò)長(zhǎng)問(wèn)題導(dǎo)致查詢(xún)變慢,JDK1.8 時(shí)在鏈表元素過(guò)多時(shí)采用紅黑樹(shù)的數(shù)據(jù)結(jié)構(gòu)。

下面我們開(kāi)始添加新元素,了解這其中的原理。

元素增加過(guò)程

當(dāng)我們往一個(gè)新字典中添加元素,默認(rèn)將會(huì)為字典中 ht[0] 哈希表分配空間,默認(rèn)情況下哈希表 table 數(shù)組大小為 4(「DICT_HT_INITIAL_SIZE」)。

新添加元素的鍵值將會(huì)經(jīng)過(guò)哈希算法,確定哈希表數(shù)組的位置,然后添加到相應(yīng)的位置,如圖所示:

Redis字典知識(shí)點(diǎn)有哪些

繼續(xù)增加元素,此時(shí)如果兩個(gè)不同鍵經(jīng)過(guò)哈希算法產(chǎn)生相同的哈希值,這樣就發(fā)生了哈希碰撞。

假設(shè)現(xiàn)在我們哈希表中擁有是三個(gè)元素,:

Redis字典知識(shí)點(diǎn)有哪些

我們?cè)僭黾右粋€(gè)新元素,如果此時(shí)剛好在數(shù)組 3 號(hào)位置上發(fā)生碰撞,此時(shí) Redis 將會(huì)采用鏈表的方式解決哈希碰撞。

Redis字典知識(shí)點(diǎn)有哪些

「注意,新元素將會(huì)放在鏈表頭結(jié)點(diǎn),這么做目的是因?yàn)樾略黾拥脑?,很大概率上?huì)被再次訪(fǎng)問(wèn),放在頭結(jié)點(diǎn)增加訪(fǎng)問(wèn)速度?!?/strong>

這里我們?cè)趯?duì)比一下元素添加過(guò)程,可以發(fā)現(xiàn) Redis 流程其實(shí)與 JDK 1.7 版本的 HashMap 類(lèi)似。

當(dāng)我們?cè)卦黾釉絹?lái)越多時(shí),哈希碰撞情況將會(huì)越來(lái)越頻繁,這就會(huì)導(dǎo)致鏈表長(zhǎng)度過(guò)長(zhǎng),極端情況下 O(1) 查詢(xún)效率退化成 O(N) 的查詢(xún)效率。

為此,字典必須進(jìn)行擴(kuò)容,這樣就會(huì)使觸發(fā)字典 rehash 操作。

擴(kuò)容

當(dāng) Redis 進(jìn)行 Rehash 擴(kuò)容操作,首先將會(huì)為字典沒(méi)有用到 ht[1] 哈希表分配更大空間。

Redis字典知識(shí)點(diǎn)有哪些
?    

畫(huà)外音:ht[1]  哈希表大小為第一個(gè)大于等于 ht[0].used*2 的 2^2(2的n 次方冪)

?

然后再將 ht[0] 中所有鍵值對(duì)都遷移到 ht[1] 中。

Redis字典知識(shí)點(diǎn)有哪些    
簡(jiǎn)單起見(jiàn),忽略指向空節(jié)點(diǎn)

當(dāng)節(jié)點(diǎn)全部遷移完畢,將會(huì)釋放 ht[0]占用空間,并將 ht[1] 設(shè)置為 ht[0]。

Redis字典知識(shí)點(diǎn)有哪些    

   

擴(kuò)容 操作需要將 ht[0]所有鍵值對(duì)都 Rehashht[1] 中,如果鍵值過(guò)多,假設(shè)存在十億個(gè)鍵值對(duì),這樣一次性的遷移,勢(shì)必導(dǎo)致服務(wù)器會(huì)在一段時(shí)間內(nèi)停止服務(wù)。

另外如果每次 rehash 都會(huì)阻塞當(dāng)前操作,這樣對(duì)于客戶(hù)端處理非常不友好。

為了避免 rehash對(duì)服務(wù)器的影響,Redis 采用漸進(jìn)式的遷移方式,慢慢將數(shù)據(jù)遷移分散到多個(gè)操作步驟。

這個(gè)操作依賴(lài)字典中一個(gè)屬性 rehashidx,這是一個(gè)索引位置計(jì)數(shù)器,記錄下一個(gè)哈希表 table 數(shù)組上元素,默認(rèn)情況為值為 「-1」。

假設(shè)此時(shí)擴(kuò)容前字典如圖所示:

Redis字典知識(shí)點(diǎn)有哪些

當(dāng)開(kāi)始 rehash 操作,rehashidx將會(huì)被設(shè)置為 「0」 。

這個(gè)期間每次收到增加,刪除,查找,更新命令,除了這些命令將會(huì)被執(zhí)行以外,還會(huì)順帶將 ht[0]哈希表在 rehashidx 位置的元素 rehash 到 ht[1] 中。

假設(shè)此時(shí)收到一個(gè) 「K3」 鍵的查詢(xún)操作,Redis 首先執(zhí)行查詢(xún)操作,接著 Redis 將會(huì)為 ht[0]哈希表上table 數(shù)組第 rehashidx索引上所有節(jié)點(diǎn)都遷移到 ht[1] 中。

Redis字典知識(shí)點(diǎn)有哪些

當(dāng)操作完成之后,再將 rehashidx 屬性值加 1。

最后當(dāng)所有鍵值對(duì)都 rehashht[1]中時(shí),rehashidx將會(huì)被重新設(shè)置為 -1。

雖然漸進(jìn)式的 rehash 操作減少了工作量,但是卻帶來(lái)鍵值操作的復(fù)雜度。

這是因?yàn)樵跐u進(jìn)式 rehash 操作期間,Redis 無(wú)法明確知道鍵到底在 ht[0]中,還是在 ht[1] 中,所以這個(gè)時(shí)候 Redis 不得不查找兩個(gè)哈希表。

以查找為例,Redis 首先查詢(xún) ht[0] ,如果沒(méi)找到將會(huì)繼續(xù)查找 ht[1],除了查詢(xún)以外,更新,刪除也會(huì)執(zhí)行如上的操作。

添加操作其實(shí)就沒(méi)這么麻煩,因?yàn)?code>ht[0]不會(huì)在使用,那就統(tǒng)一都添加到 ht[1] 中就好了。

最后我們?cè)賹?duì)比一下 Java HashMap 擴(kuò)容操作,它是一個(gè)一次性操作,每次擴(kuò)容需要將所有鍵值對(duì)都遷移到新的數(shù)組中,所以如果數(shù)據(jù)量很大,消耗時(shí)間就會(huì)久。

感謝各位的閱讀,以上就是“Redis字典知識(shí)點(diǎn)有哪些”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)Redis字典知識(shí)點(diǎn)有哪些這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI