Redis之常用數(shù)據(jù)結(jié)構(gòu)哈希表
哈希表是一種保存鍵值對(duì)(key-value)的數(shù)據(jù)結(jié)構(gòu)
哈希表優(yōu)點(diǎn)在于,它能以 O(1) 的復(fù)雜度快速查詢數(shù)據(jù)。
怎么做到的呢?
將 key 通過 Hash 函數(shù)的計(jì)算,就能定位數(shù)據(jù)在表中的位置,因?yàn)楣1韺?shí)際上是數(shù)組,所以可以通過索引值快速查詢到數(shù)據(jù)。
在哈希表大小固定的情況下,隨著數(shù)據(jù)不斷增多,那么哈希沖突的可能性也會(huì)越高。
Redis 采用了**「鏈?zhǔn)焦!?*來解決哈希沖突,在不擴(kuò)容哈希表的前提下,將具有相同哈希值的數(shù)據(jù)串起來,形成鏈接起,以便這些數(shù)據(jù)在表中仍然可以被查詢到
1.哈希沖突
哈希表實(shí)際上是一個(gè)數(shù)組,數(shù)組里的每一個(gè)元素就是一個(gè)哈希桶
當(dāng)一個(gè)鍵值對(duì)的鍵經(jīng)過 Hash 函數(shù)計(jì)算后得到哈希值,再將(哈希值 % 哈希表大小)取模計(jì)算,得到的結(jié)果值就是該 key-value 對(duì)應(yīng)的數(shù)組元素位置,也就是第幾個(gè)哈希桶
當(dāng)有兩個(gè)以上數(shù)量的 kay 被分配到了哈希表中同一個(gè)哈希桶上時(shí),此時(shí)稱這些 key 發(fā)生了沖突
2.鏈?zhǔn)焦?/h2>
鏈?zhǔn)焦J窃趺磳?shí)現(xiàn)的?
實(shí)現(xiàn)的方式就是每個(gè)哈希表節(jié)點(diǎn)都有一個(gè) next 指針,用于指向下一個(gè)哈希表節(jié)點(diǎn),因此多個(gè)哈希表節(jié)點(diǎn)可以用 next指針構(gòu)成一個(gè)單項(xiàng)鏈表,被分配到同一個(gè)哈希桶上的多個(gè)節(jié)點(diǎn)可以用這個(gè)單項(xiàng)鏈表連接起來,這樣就解決了哈希沖突。
鏈?zhǔn)焦J窃趺磳?shí)現(xiàn)的?
實(shí)現(xiàn)的方式就是每個(gè)哈希表節(jié)點(diǎn)都有一個(gè) next 指針,用于指向下一個(gè)哈希表節(jié)點(diǎn),因此多個(gè)哈希表節(jié)點(diǎn)可以用 next指針構(gòu)成一個(gè)單項(xiàng)鏈表,被分配到同一個(gè)哈希桶上的多個(gè)節(jié)點(diǎn)可以用這個(gè)單項(xiàng)鏈表連接起來,這樣就解決了哈希沖突。
隨著鏈表長(zhǎng)度的增加,在查詢這一位置上的數(shù)據(jù)的耗時(shí)就會(huì)增加,因?yàn)殒湵淼牟樵兊臅r(shí)間復(fù)雜度是 O(n)。
3.rehash
Redis 定義一個(gè) dict 結(jié)構(gòu)體,這個(gè)結(jié)構(gòu)體里定義了兩個(gè)哈希表(ht[2])
之所以定義了 2 個(gè)哈希表,是因?yàn)檫M(jìn)行 rehash 的時(shí)候,需要用上 2 個(gè)哈希表
在正常服務(wù)請(qǐng)求階段,插入的數(shù)據(jù),都會(huì)寫入到「哈希表 1」,此時(shí)的「哈希表 2 」 并沒有被分配空間。
隨著數(shù)據(jù)逐步增多,觸發(fā)了 rehash 操作,這個(gè)過程分為三步:
1.給「哈希表 2」 分配空間,一般會(huì)比「哈希表 1」 大 2 倍;
2.將「哈希表 1 」的數(shù)據(jù)遷移到「哈希表 2」 中;
3.遷移完成后,「哈希表 1 」的空間會(huì)被釋放,并把「哈希表 2」 設(shè)置為「哈希表 1」,然后在「哈希表 2」 新創(chuàng)建一個(gè)空白的哈希表,為下次 rehash 做準(zhǔn)備。
第二步很有問題,如果「哈希表 1 」的數(shù)據(jù)量非常大,那么在遷移至「哈希表 2 」的時(shí)候,因?yàn)闀?huì)涉及大量的數(shù)據(jù)拷貝,此時(shí)可能會(huì)對(duì) Redis 造成阻塞,無法服務(wù)其他請(qǐng)求
4.漸進(jìn)式 rehash
為了避免 rehash 在數(shù)據(jù)遷移過程中,因拷貝數(shù)據(jù)的耗時(shí),影響 Redis 性能的情況
漸進(jìn)式 rehash 步驟如下:
1.給「哈希表 2」 分配空間;
2.在 rehash 進(jìn)行期間,每次哈希表元素進(jìn)行新增、刪除、查找或者更新操作時(shí),Redis 除了會(huì)執(zhí)行對(duì)應(yīng)的操作之外,還會(huì)順序?qū)ⅰ腹1?1 」中索引位置上的所有 key-value 遷移到「哈希表 2」 上;
3.隨著處理客戶端發(fā)起的哈希表操作請(qǐng)求數(shù)量越多,最終在某個(gè)時(shí)間點(diǎn)會(huì)把「哈希表 1 」的所有 key-value 遷移到「哈希表 2」,從而完成 rehash 操作。
把一次性大量數(shù)據(jù)遷移工作的開銷,分?jǐn)偟搅硕啻翁幚碚?qǐng)求的過程中,避免了一次性 rehash 的耗時(shí)操作
1.查找一個(gè) key 的值的話,先會(huì)在「哈希表 1」 里面進(jìn)行查找,如果沒找到,就會(huì)繼續(xù)到哈希表 2 里面進(jìn)行找到。
2.新增一個(gè) key-value 時(shí),會(huì)被保存到「哈希表 2 」里面,而「哈希表 1」 則不再進(jìn)行任何添加操作,這樣保證了「哈希表 1 」的 key-value 數(shù)量只會(huì)減少,隨著 rehash 操作的完成,最終「哈希表 1 」就會(huì)變成空表
5.rehash 觸發(fā)條件
觸發(fā)條件跟**負(fù)載因子(load factor)**有關(guān)系。

主要有兩個(gè):
1.當(dāng)負(fù)載因子大于等于 1 ,并且 Redis 沒有在執(zhí)行 bgsave 命令或者 bgrewiteaof 命令,也就是沒有執(zhí)行 RDB 快照或沒有進(jìn)行 AOF 重寫的時(shí)候,就會(huì)進(jìn)行 rehash 操作。
2.當(dāng)負(fù)載因子大于等于 5 時(shí),此時(shí)說明哈希沖突非常嚴(yán)重了,不管有沒有有在執(zhí)行 RDB 快照或 AOF 重寫,都會(huì)強(qiáng)制進(jìn)行 rehash 操作
到此這篇關(guān)于Redis之常用數(shù)據(jù)結(jié)構(gòu)哈希表的文章就介紹到這了,更多相關(guān)Redis哈希表內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Redis過期Key刪除策略和內(nèi)存淘汰策略的實(shí)現(xiàn)
當(dāng)內(nèi)存使用達(dá)到上限,就無法存儲(chǔ)更多數(shù)據(jù)了,為了解決這個(gè)問題,Redis內(nèi)部會(huì)有兩套內(nèi)存回收的策略,過期Key刪除策略和內(nèi)存淘汰策略,本文就來詳細(xì)的介紹一下這兩種方法,感興趣的可以了解一下2024-02-02
Redis內(nèi)存空間占用及避免數(shù)據(jù)丟失的方法
在現(xiàn)代的互聯(lián)網(wǎng)應(yīng)用中,Redis作為一種高性能的內(nèi)存數(shù)據(jù)庫(kù),被廣泛應(yīng)用于緩存、會(huì)話管理和消息隊(duì)列等場(chǎng)景,然而,Redis的內(nèi)存資源是有限的,過多的內(nèi)存占用可能會(huì)導(dǎo)致數(shù)據(jù)丟失所以本文將給大家介紹一下Redis內(nèi)存空間占用及避免數(shù)據(jù)丟失的方法2023-08-08
Windows環(huán)境下打開Redis閃退的解決方案
每次使用完Redis后,我們習(xí)慣性的動(dòng)作是直接叉掉doc頁(yè)面,這樣導(dǎo)致的結(jié)果是Redis在后臺(tái)繼續(xù)運(yùn)行,沒有關(guān)閉,所以當(dāng)再次打開的時(shí)候直接閃退,文中有詳細(xì)的解決方案,需要的朋友可以參考下2024-03-03

