Redis之常用數(shù)據(jù)結(jié)構(gòu)哈希表
哈希表是一種保存鍵值對(duì)(key-value)的數(shù)據(jù)結(jié)構(gòu)
哈希表優(yōu)點(diǎn)在于,它能以 O(1) 的復(fù)雜度快速查詢數(shù)據(jù)。
怎么做到的呢?
將 key 通過(guò) Hash 函數(shù)的計(jì)算,就能定位數(shù)據(jù)在表中的位置,因?yàn)楣1韺?shí)際上是數(shù)組,所以可以通過(guò)索引值快速查詢到數(shù)據(jù)。
在哈希表大小固定的情況下,隨著數(shù)據(jù)不斷增多,那么哈希沖突的可能性也會(huì)越高。
Redis 采用了**「鏈?zhǔn)焦!?*來(lái)解決哈希沖突,在不擴(kuò)容哈希表的前提下,將具有相同哈希值的數(shù)據(jù)串起來(lái),形成鏈接起,以便這些數(shù)據(jù)在表中仍然可以被查詢到
1.哈希沖突
哈希表實(shí)際上是一個(gè)數(shù)組,數(shù)組里的每一個(gè)元素就是一個(gè)哈希桶
當(dāng)一個(gè)鍵值對(duì)的鍵經(jīng)過(guò) 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)鏈表連接起來(lái),這樣就解決了哈希沖突。
鏈?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)鏈表連接起來(lái),這樣就解決了哈希沖突。
隨著鏈表長(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ì)寫(xiě)入到「哈希表 1」,此時(shí)的「哈希表 2 」 并沒(méi)有被分配空間。
隨著數(shù)據(jù)逐步增多,觸發(fā)了 rehash 操作,這個(gè)過(guò)程分為三步:
1.給「哈希表 2」 分配空間,一般會(huì)比「哈希表 1」 大 2 倍;
2.將「哈希表 1 」的數(shù)據(jù)遷移到「哈希表 2」 中;
3.遷移完成后,「哈希表 1 」的空間會(huì)被釋放,并把「哈希表 2」 設(shè)置為「哈希表 1」,然后在「哈希表 2」 新創(chuàng)建一個(gè)空白的哈希表,為下次 rehash 做準(zhǔn)備。
第二步很有問(wèn)題,如果「哈希表 1 」的數(shù)據(jù)量非常大,那么在遷移至「哈希表 2 」的時(shí)候,因?yàn)闀?huì)涉及大量的數(shù)據(jù)拷貝,此時(shí)可能會(huì)對(duì) Redis 造成阻塞,無(wú)法服務(wù)其他請(qǐng)求
4.漸進(jìn)式 rehash
為了避免 rehash 在數(shù)據(jù)遷移過(guò)程中,因拷貝數(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ù)遷移工作的開(kāi)銷(xiāo),分?jǐn)偟搅硕啻翁幚碚?qǐng)求的過(guò)程中,避免了一次性 rehash 的耗時(shí)操作
1.查找一個(gè) key 的值的話,先會(huì)在「哈希表 1」 里面進(jìn)行查找,如果沒(méi)找到,就會(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 沒(méi)有在執(zhí)行 bgsave 命令或者 bgrewiteaof 命令,也就是沒(méi)有執(zhí)行 RDB 快照或沒(méi)有進(jìn)行 AOF 重寫(xiě)的時(shí)候,就會(huì)進(jìn)行 rehash 操作。
2.當(dāng)負(fù)載因子大于等于 5 時(shí),此時(shí)說(shuō)明哈希沖突非常嚴(yán)重了,不管有沒(méi)有有在執(zhí)行 RDB 快照或 AOF 重寫(xiě),都會(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內(nèi)部數(shù)據(jù)結(jié)構(gòu)Dict的實(shí)現(xiàn)方法
這篇文章主要介紹了Redis內(nèi)部數(shù)據(jù)結(jié)構(gòu)Dict的實(shí)現(xiàn)方法,本篇文章所述的dict在Redis中最主要的作用就是用于維護(hù)Redis數(shù)據(jù)庫(kù)中所有Key、value映射的數(shù)據(jù)結(jié)構(gòu),需要的朋友可以參考下2022-05-05linux 常見(jiàn)的標(biāo)識(shí)與Redis數(shù)據(jù)庫(kù)詳解
這篇文章主要介紹了linux 常見(jiàn)的標(biāo)識(shí)與Redis數(shù)據(jù)庫(kù),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-10-10手動(dòng)實(shí)現(xiàn)Redis的LRU緩存機(jī)制示例詳解
這篇文章主要介紹了手動(dòng)實(shí)現(xiàn)Redis的LRU緩存機(jī)制示例詳解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03Redis中set類(lèi)型實(shí)現(xiàn)交集并集差集
本文主要介紹了Redis中set類(lèi)型實(shí)現(xiàn)交集并集差集,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06Linux下安裝Redis并設(shè)置相關(guān)服務(wù)
這篇文章主要為大家介紹了Linux下安裝Redis并設(shè)置相關(guān)服務(wù),感興趣的小伙伴們可以參考一下2016-01-01redis?bitmap數(shù)據(jù)結(jié)構(gòu)之java對(duì)等操作詳解
bitmap是以其高性能出名。其基本原理是一位存儲(chǔ)一個(gè)標(biāo)識(shí),其他衍生知道咱就不說(shuō)了,而redis就是以這種原生格式存儲(chǔ)的,這篇文章主要介紹了redis?bitmap數(shù)據(jù)結(jié)構(gòu)之java對(duì)等操作,需要的朋友可以參考下2022-10-10Redis序列化轉(zhuǎn)換類(lèi)型報(bào)錯(cuò)的解決
本文主要介紹了Redis序列化轉(zhuǎn)換類(lèi)型報(bào)錯(cuò)的解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04redis主從切換導(dǎo)致的數(shù)據(jù)丟失與陷入只讀狀態(tài)故障解決方案
這篇文章主要介紹了redis主從切換導(dǎo)致的數(shù)據(jù)丟失與陷入只讀狀態(tài)故障解決方案的相關(guān)資料,需要的朋友可以參考下2023-05-05