摘要:本文共字,閱讀大約需要分鐘概述在前文字符串類型內(nèi)部編碼剖析之中已經(jīng)剖析過(guò)最基本的類型的內(nèi)部是怎么編碼和存儲(chǔ)的,本文再來(lái)闡述中使用最為頻繁的數(shù)據(jù)類型哈?;蚍Q散列,在內(nèi)部是怎么存的。
本文共 1231字,閱讀大約需要 5分鐘 !
在前文《Redis字符串類型內(nèi)部編碼剖析》之中已經(jīng)剖析過(guò) Redis最基本的 String類型的內(nèi)部是怎么編碼和存儲(chǔ)的,本文再來(lái)闡述 Redis中使用 最為頻繁的數(shù)據(jù)類型:哈希(或稱散列),在Redis內(nèi)部是怎么存的。
實(shí)驗(yàn)源碼環(huán)境:Redis 4.0.10
注: 本文首發(fā)于 My Personal Blog,歡迎光臨 小站
本文內(nèi)容腦圖如下:
對(duì)于 Redis的常用 5 種數(shù)據(jù)類型(String、Hash、List、Set、sorted set),每種數(shù)據(jù)類型都提供了 最少兩種 內(nèi)部的編碼格式,而且每個(gè)數(shù)據(jù)類型內(nèi)部編碼方式的選擇 對(duì)用戶是完全透明的,Redis會(huì)根據(jù)數(shù)據(jù)量自適應(yīng)地選擇較優(yōu)化的內(nèi)部編碼格式。
如果想查看某個(gè)鍵的內(nèi)部編碼格式,可以使用 OBJECT ENCODING keyname 指令來(lái)進(jìn)行,比如:
127.0.0.1:6379> 127.0.0.1:6379> set foo bar OK 127.0.0.1:6379> 127.0.0.1:6379> object encoding foo // 查看某個(gè)Redis鍵值的編碼 "embstr" 127.0.0.1:6379> 127.0.0.1:6379>
對(duì)于使用最為頻繁的 Hash類型,其內(nèi)部編碼方式可能有兩種:
OBJ_ENCODING_ZIPLIST(壓縮列表)
OBJ_ENCODING_HT(哈希表)
Redis 會(huì)根據(jù)數(shù)據(jù)量的情況來(lái)自適應(yīng)地選擇這兩種編碼方式中 較優(yōu) 的一種,而這一切對(duì)用戶完全透明。
在 數(shù)據(jù)條目較少,數(shù)據(jù)值較小 的時(shí)候 Redis會(huì)采用 壓縮列表(OBJ_ENCODING_ZIPLIST)編碼方式進(jìn)行存儲(chǔ)。這里成員"較少",成員值"較小"的標(biāo)準(zhǔn)可以通過(guò)如下配置項(xiàng)進(jìn)行配置:
hash-max-ziplist-entries 512 hash-max-ziplist-value 64
Redis 默認(rèn)給出了默認(rèn)值,當(dāng)然用戶可根據(jù)實(shí)際情況自行配置。
當(dāng) Hash類型鍵的字段個(gè)數(shù) < hash-max-ziplist-entries 并且 每個(gè)字段名和字段值的長(zhǎng)度 < hash-max-ziplist-value 時(shí),Redis 會(huì)使用 OBJ_ENCODING_ZIPLIST來(lái)存儲(chǔ)該鍵,反之則會(huì)轉(zhuǎn)換為 OBJ_ENCODING_HT的編碼方式。
口說(shuō)無(wú)憑,我們不妨先來(lái)做個(gè)實(shí)驗(yàn)感受一下吧:
很明顯該實(shí)驗(yàn)驗(yàn)證了當(dāng) 字段值長(zhǎng)度大于64時(shí),編碼格式會(huì)由 ZIPLIST方式切換為 Hashtable方式。
源碼之前,了無(wú)秘密,我們?cè)賮?lái)看一下Redis關(guān)于這部分切換的源碼實(shí)現(xiàn),那就理解得更加清楚了:
下面詳解 OBJ_ENCODING_ZIPLIST 和 OBJ_ENCODING_HT 這兩種編碼格式的內(nèi)部存儲(chǔ)模型,知道了其各自特點(diǎn)和優(yōu)缺點(diǎn),自然也就明白了Redis內(nèi)部使用它們的意圖。
Ziplist 壓縮列表是一種緊湊編碼格式,總體思想是時(shí)間換空間,即以部分讀寫(xiě)性能為代價(jià),來(lái)?yè)Q取極高的內(nèi)存空間利用率,因此只會(huì)用于 字段個(gè)數(shù)少,且字段值也較小 的場(chǎng)景。
壓縮列表內(nèi)存利用率極高的原因與其連續(xù)內(nèi)存的特性是分不開(kāi)的,其典型的內(nèi)存結(jié)構(gòu)可以用下圖形象地展示出來(lái):
所以如果用 Ziplist來(lái)存儲(chǔ) Redis的散列類型的話,元素的排列方式就變成了如下圖所示的形象示意圖:即key和value都是邏輯連續(xù)內(nèi)存:
OBJ_ENCODING_HT 這種編碼方式內(nèi)部才是真正的哈希表結(jié)構(gòu),或稱為字典結(jié)構(gòu),其可以實(shí)現(xiàn)O(1)復(fù)雜度的讀寫(xiě)操作,因此效率很高。
在 Redis內(nèi)部,從 OBJ_ENCODING_HT類型到底層真正的散列表數(shù)據(jù)結(jié)構(gòu)是一層層嵌套下去的,關(guān)系如下:
這一關(guān)系我們可以從 Redis哈希表定義部分的源碼來(lái)看出:
下面來(lái)詳解一下各個(gè)部分:
關(guān)于哈希節(jié)點(diǎn)(dictEntry)
關(guān)于哈希表(dictht)和字典(dict)
關(guān)于dictType
Redis如何計(jì)算Hash值
Redis計(jì)算Hash的源代碼如下:
這是一個(gè) C語(yǔ)言宏定義,其實(shí)幕后真正承擔(dān) Hash值計(jì)算的是上面介紹的 dictType結(jié)構(gòu)體中的函數(shù)指針 hashFunction。
而該 hashFunction函數(shù)指針在初始化時(shí)會(huì)對(duì)應(yīng)被賦值為一個(gè)個(gè)真實(shí)的計(jì)算 Hash值的實(shí)際函數(shù),就像下面這樣:
Redis如何計(jì)算存取索引Index值
Index值的計(jì)算依賴于上面計(jì)算得出的 Hash值,代碼如下:
到此,還有一個(gè)一直非常值得關(guān)注的細(xì)節(jié):即字典 dict里總是保存有兩個(gè) Hash表結(jié)構(gòu) ht[2],以及與其高度相關(guān)的 rehash操作,這在下一篇文章里詳解。
由于能力有限,若有錯(cuò)誤或者不當(dāng)之處,還請(qǐng)大家批評(píng)指正,一起學(xué)習(xí)交流!
My Personal Blog
我的半年技術(shù)博客之路
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/17777.html
閱讀 2846·2023-04-26 02:23
閱讀 1594·2021-11-11 16:55
閱讀 3155·2021-10-19 11:47
閱讀 3370·2021-09-22 15:15
閱讀 1984·2019-08-30 15:55
閱讀 1045·2019-08-29 15:43
閱讀 1299·2019-08-29 13:16
閱讀 2203·2019-08-29 12:38