最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

淺析Redis底層數(shù)據(jù)結(jié)構(gòu)Dict

 更新時(shí)間:2023年05月30日 09:23:12   作者:WARRIOR  
Redis是一個(gè)鍵值型的數(shù)據(jù)庫(kù),我們可以根據(jù)鍵實(shí)現(xiàn)快速的增刪改查,而鍵與值的映射關(guān)系正是通過(guò)Dict來(lái)實(shí)現(xiàn)的,當(dāng)然?Dict?也是?Set?Hash?的實(shí)現(xiàn)方式,本文就詳細(xì)帶大家介紹一下Redis底層數(shù)據(jù)結(jié)構(gòu)?Dict,,需要的朋友可以參考下

Dict 優(yōu)點(diǎn)在于,它能以 O(1) 的復(fù)雜度快速查詢數(shù)據(jù)。怎么做到的呢?將 key 通過(guò) Hash 函數(shù)的計(jì)算,就能定位數(shù)據(jù)在表中的位置,因?yàn)楣1韺?shí)際上是數(shù)組,所以可以通過(guò)索引值快速查詢到數(shù)據(jù)。

但是存在的風(fēng)險(xiǎn)也是有,在哈希表大小固定的情況下,隨著數(shù)據(jù)不斷增多,那么哈希沖突的可能性也會(huì)越高。

解決哈希沖突的方式,有很多種。

Redis 采用了「鏈?zhǔn)焦!箒?lái)解決哈希沖突,在不擴(kuò)容哈希表的前提下,將具有相同哈希值的數(shù)據(jù)串起來(lái),形成鏈接起,以便這些數(shù)據(jù)在表中仍然可以被查詢到。

接下來(lái),詳細(xì)說(shuō)說(shuō) Dict 的結(jié)構(gòu)設(shè)計(jì)

Dict 的結(jié)構(gòu)

Dict 由三部分組成,分別是:dictdictht、dicEntry

dictht

dictht 的結(jié)構(gòu)如下:

typedef struct dictht {
    dictEntry **table;
    unsigned long size;
    unsigned long sizemask;
    unsigned long used;
} dictht;
  • dictEntry **table,哈希表數(shù)組
  • unsigned long size,哈希表大小(取值為 2n2^n2n)
  • unsigned long sizemask,哈希表大小掩碼,用于計(jì)算索引值,總是等于 size−1size - 1size−1
  • unsigned long used,該哈希表已有的節(jié)點(diǎn)數(shù)量

dicEntry

dicEntry 結(jié)構(gòu)如下

void *key;/*鍵*/
    union {
        void *val;
        uint64_t u64;
        int64_t s64;
        double d;
    } v; /*值*/
    struct dictEntry *next;/*下一個(gè) entry 的指針*/
} dictEntry;
  • dicEntry 和 dictht 之間的組織方式如下圖所示

  • 當(dāng)我們向 Dict 添加鍵值對(duì)時(shí),Redis 首先根據(jù) key 計(jì)算出 hash值(h),然后利用 h & sizemask 來(lái)計(jì)算元素應(yīng)該存儲(chǔ)到數(shù)組中的哪個(gè)索引位置。我們存儲(chǔ) k1=v1,假設(shè) k1 的哈希值 h =1,則 1&3 = 1,因此 k1=v1 要存儲(chǔ)到數(shù)組角標(biāo) 1 位置。
  • 如果計(jì)算出來(lái)的數(shù)組角標(biāo)值相同,也就是說(shuō),出現(xiàn)了 *哈希沖突,redis 采用 ”鏈?zhǔn)焦?ldquo; 的方式,將具有相同哈希值的數(shù)據(jù)串起來(lái),形成鏈結(jié)構(gòu),這也就是為什么會(huì)有 struct dictEntry next 這個(gè)成員變量存在

?? 為什么是 h & sizemask ? 在根據(jù) hash 值(h)來(lái)計(jì)算應(yīng)該把 entry 放在哪個(gè)數(shù)組下標(biāo)位置時(shí),你可能會(huì)好奇,為什么不是使用 h%size ,而是使用 h&sizemask,而他們?yōu)槭裁纯梢缘贸鲆粯拥慕Y(jié)果。
實(shí)際上,當(dāng)散列表的大小為 2n2^n2n 時(shí),h%sizemask 的結(jié)果與 h%size 是相同的(這里不做證明)。讓我們以 size 為 8 的散列表為例:

  • size = 8,對(duì)應(yīng)的 sizemask = 7 (111的二進(jìn)制表示)
  • h = 18 (10010的二進(jìn)制表示)
  • h%size = 18%8 = 2
  • h&sizemask = 18&7 = 2

dict

在實(shí)際使用哈希表時(shí),Redis 沒(méi)有使用 dictht ,而是定義一個(gè) dict 結(jié)構(gòu)體,如下

typedef struct dict {
    dictType *type; /* dict類型,內(nèi)置不同的hash函數(shù) */
    void *privdata; /* 私有數(shù)據(jù),在做特殊hash運(yùn)算時(shí)用 */
    dictht ht[2] ;/* 個(gè)Dict包含兩個(gè)哈希表,其中一個(gè)是當(dāng)前數(shù)據(jù),另一個(gè)一般是空,rehash時(shí)使用 */
    long rehashidx; /* rehash的進(jìn)度,-1表示未進(jìn)行 */
    int16_t pauserehash; /* rehash是否暫停,1則暫停,0則繼續(xù) */
} dict;
  • 在上面這個(gè)結(jié)構(gòu)體中,我們發(fā)現(xiàn),type 、privdata 是跟哈希運(yùn)算有關(guān)系的,但是其他三個(gè)成員變量,又是用來(lái)做什么的呢?為什么又要定義兩個(gè) dictht 呢?這跟我們下面要說(shuō)的 rehash 操作有關(guān)系

Dict 的 rehash

前面我們提到,redis 使用鏈?zhǔn)焦?lái)解決 hash 沖突問(wèn)題。但是,鏈?zhǔn)焦R泊嬖诰窒扌裕蔷褪?strong>隨著鏈表長(zhǎng)度的增加,Hash 表在一個(gè)位置上查詢哈希項(xiàng)的耗時(shí)就會(huì)增加,從而增加了 Hash 表的整體查詢時(shí)間,這樣也會(huì)導(dǎo)致 Hash 表的性能下降。這時(shí),redis 使用 rehash 來(lái)解決這個(gè)問(wèn)題。

Redis 如何實(shí)現(xiàn) rehash

Redis 實(shí)現(xiàn) rehash 的基本思路是這樣的:

  • 首先,Redis 準(zhǔn)備了兩個(gè)哈希表,用于 rehash 時(shí)交替保存數(shù)據(jù)。

    • 前面我們提到,redis 在實(shí)際使用時(shí),定義了一個(gè) dict 結(jié)構(gòu)體。這個(gè)結(jié)構(gòu)體中有一個(gè)數(shù)組(*ht[2] *),包含了兩個(gè) Hash 表(dictht ) *ht[0] *和 *ht[1] *。
  • 其次,在正常服務(wù)請(qǐng)求階段,所有的鍵值對(duì)寫入哈希表 ht[0]。

  • 接著,當(dāng)進(jìn)行 rehash 時(shí),鍵值對(duì)被遷移到哈希表 ht[1]中。

  • 最后,當(dāng)遷移完成后,ht[0]的空間會(huì)被釋放,并把 ht[1] 的地址賦值給 ht[0],ht[1] 的表大小設(shè)置為 0。這樣一來(lái),又回到了正常服務(wù)請(qǐng)求的階段,ht[0] 接收和服務(wù)請(qǐng)求,ht[1] 作為下一次 rehash 時(shí)的遷移表。

什么時(shí)候進(jìn)行 rehash

  • 當(dāng)我們往 Redis 中寫入新的鍵值對(duì)或是修改鍵值對(duì)時(shí),Redis 都會(huì)判斷下是否需要進(jìn)行 rehash。而 rehash 的觸發(fā)條件則是

    • 條件 1 :ht[0] 承載的元素個(gè)數(shù)已經(jīng)超過(guò)了 ht[0] 的大小,也即d->ht[0].used >= d->ht[0].size,同時(shí) Hash 表可以進(jìn)行擴(kuò)容。
    • 條件 2 :ht[0] 承載的元素個(gè)數(shù),是 ht[0] 的大小的 dict_force_resize_ratio 倍,也即 d->ht[0].used/d->ht[0].size > dict_force_resize_ratio 其中,dict_force_resize_ratio 的默認(rèn)值是 5。

rehash 的新 size 是多大?

如果是擴(kuò)容,則新 size 為第一個(gè)大于等于 dict.ht[0].used+1 的2n2^n2n 如果是收縮,則新 size 為第一個(gè)大于等于 dict.ht[0].used 的 2n2^n2n(不得小于4)

漸進(jìn)式 rehash

  • Hash 表在執(zhí)行 rehash 時(shí),由于 Hash 表空間擴(kuò)大,原本映射到某一位置的鍵可能會(huì)被映射到一個(gè)新的位置上,因此,很多鍵就需要從原來(lái)的位置拷貝到新的位置。而在鍵拷貝時(shí),由于 Redis 主線程無(wú)法執(zhí)行其他請(qǐng)求,所以鍵拷貝會(huì)阻塞主線程,這樣就會(huì)產(chǎn)生 rehash 開銷。為了降低 rehash 開銷,Redis 就提出了漸進(jìn)式 rehash 的方法。

rehash 的步驟

  • 給 ht[1] 分配空間;
  • 在 rehash 進(jìn)行期間,在rehash過(guò)程中,新增操作,則直接寫入 ht[1],查詢、修改和刪除則會(huì)在dict.ht[0]dict.ht[1] 依次查找并執(zhí)行。這樣可以確保 ht[0] 的數(shù)據(jù)只減不增。
  • 隨著處理客戶端發(fā)起的哈希表操作請(qǐng)求數(shù)量越多,最終在某個(gè)時(shí)間點(diǎn)會(huì)把 ht[0] 的所有 key-value 遷移到 ht[1],從而完成 rehash 操作。

這樣就巧妙地把一次性大量數(shù)據(jù)遷移工作的開銷,分?jǐn)偟搅硕啻翁幚碚?qǐng)求的過(guò)程中,避免了一次性 rehash 的耗時(shí)操作。

以上就是淺析Redis底層數(shù)據(jù)結(jié)構(gòu)Dict的詳細(xì)內(nèi)容,更多關(guān)于Redis數(shù)據(jù)結(jié)構(gòu)Dict的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 基于Redis分布式鎖的實(shí)現(xiàn)代碼

    基于Redis分布式鎖的實(shí)現(xiàn)代碼

    這篇文章主要介紹了Redis分布式鎖的實(shí)現(xiàn),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-05-05
  • Redis Cluster模式配置

    Redis Cluster模式配置

    這篇文章主要介紹了Redis Cluster模式配置,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2025-06-06
  • redis部署及各種數(shù)據(jù)類型使用命令詳解

    redis部署及各種數(shù)據(jù)類型使用命令詳解

    這篇文章主要介紹了redis部署及各種數(shù)據(jù)類型使用命令,編譯安裝redis及部署過(guò)程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-03-03
  • Redis哨兵模式介紹

    Redis哨兵模式介紹

    這篇文章介紹了Redis哨兵模式,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-02-02
  • redis實(shí)現(xiàn)簡(jiǎn)單分布式鎖

    redis實(shí)現(xiàn)簡(jiǎn)單分布式鎖

    這篇文章主要介紹了redis實(shí)現(xiàn)簡(jiǎn)單分布式鎖,文中通過(guò)代碼示例講解的非常詳細(xì),需要的朋友可以參考下
    2013-09-09
  • Redis事務(wù)為什么不支持回滾

    Redis事務(wù)為什么不支持回滾

    事務(wù)是關(guān)系型數(shù)據(jù)庫(kù)的特征之一,那么作為 Nosql 的代表 Redis 中有事務(wù)嗎?如果有,那么 Redis 當(dāng)中的事務(wù)又是否具備關(guān)系型數(shù)據(jù)庫(kù)的 ACID 四大特性,本文就來(lái)詳細(xì)介紹一下
    2021-08-08
  • Redis數(shù)據(jù)編碼詳解

    Redis數(shù)據(jù)編碼詳解

    這篇文章主要介紹了Redis數(shù)據(jù)編碼的相關(guān)知識(shí),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2026-03-03
  • Redis+Caffeine實(shí)現(xiàn)高效兩級(jí)緩存架構(gòu)的詳細(xì)指南

    Redis+Caffeine實(shí)現(xiàn)高效兩級(jí)緩存架構(gòu)的詳細(xì)指南

    在現(xiàn)代高并發(fā)系統(tǒng)中,緩存是提升系統(tǒng)性能的關(guān)鍵組件之一,本文將介紹如何結(jié)合 Redis 和 Caffeine 構(gòu)建一個(gè)高效的兩級(jí)緩存系統(tǒng),需要的小伙伴可以了解下
    2025-07-07
  • springboot項(xiàng)目redis緩存異常實(shí)戰(zhàn)案例詳解(提供解決方案)

    springboot項(xiàng)目redis緩存異常實(shí)戰(zhàn)案例詳解(提供解決方案)

    redis基本上是高并發(fā)場(chǎng)景上會(huì)用到的一個(gè)高性能的key-value數(shù)據(jù)庫(kù),屬于nosql類型,一般用作于緩存,一般是結(jié)合數(shù)據(jù)庫(kù)一塊使用的,但是在使用的過(guò)程中可能會(huì)出現(xiàn)異常的問(wèn)題,這篇文章主要介紹了springboot項(xiàng)目redis緩存異常實(shí)戰(zhàn)案例詳解(提供解決方案),需要的朋友可以參考下
    2025-05-05
  • redis中session會(huì)話共享的三種方案

    redis中session會(huì)話共享的三種方案

    本文探討了分布式系統(tǒng)中Session共享的三種解決方案,包括粘性會(huì)話、Session復(fù)制以及基于Redis的集中存儲(chǔ),具有一定的參考價(jià)值,感興趣的可以了解一下
    2025-08-08

最新評(píng)論

女性| 汾阳市| 航空| 西平县| 荥阳市| 台东县| 柳州市| 太湖县| 丹阳市| 龙泉市| 南昌市| 阳江市| 中卫市| 文水县| 托克逊县| 淮安市| 调兵山市| 揭西县| 茂名市| 威信县| 八宿县| 铜川市| 会同县| 临沭县| 分宜县| 兰溪市| 大港区| 许昌市| 广宗县| 凤冈县| 老河口市| 东乌| 商城县| 河东区| 吉林省| 南江县| 堆龙德庆县| 青铜峡市| 连江县| 沅江市| 肥乡县|