本文共 1231字,閱讀大約須要 5分鐘 !數據結構
在前文《Redis字符串類型內部編碼剖析》之中已經剖析過 Redis最基本的 String類型的內部是怎麼編碼和存儲的,本文再來闡述 Redis中使用 最爲頻繁的數據類型:哈希(或稱散列),在Redis內部是怎麼存的。函數
注: 本文首發於 My Personal Blog,歡迎光臨 小站性能
本文內容腦圖以下:學習
對於 Redis的經常使用 5 種數據類型(String、Hash、List、Set、sorted set),每種數據類型都提供了 最少兩種 內部的編碼格式,並且每一個數據類型內部編碼方式的選擇 對用戶是徹底透明的,Redis會根據數據量自適應地選擇較優化的內部編碼格式。優化
若是想查看某個鍵的內部編碼格式,可使用 OBJECT ENCODING keyname
指令來進行,好比:編碼
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 // 查看某個Redis鍵值的編碼 "embstr" 127.0.0.1:6379> 127.0.0.1:6379>
對於使用最爲頻繁的 Hash類型,其內部編碼方式可能有兩種:3d
Redis 會根據數據量的狀況來自適應地選擇這兩種編碼方式中 較優 的一種,而這一切對用戶徹底透明。指針
在 數據條目較少,數據值較小 的時候 Redis會採用 壓縮列表(OBJ_ENCODING_ZIPLIST)編碼方式進行存儲。這裏成員"較少",成員值"較小"的標準能夠經過以下配置項進行配置:code
hash-max-ziplist-entries 512 hash-max-ziplist-value 64
Redis 默認給出了默認值,固然用戶可根據實際狀況自行配置。blog
當 Hash類型鍵的字段個數 < hash-max-ziplist-entries
而且 每一個字段名和字段值的長度 < hash-max-ziplist-value
時,Redis 會使用 OBJ_ENCODING_ZIPLIST來存儲該鍵,反之則會轉換爲 OBJ_ENCODING_HT的編碼方式。
口說無憑,咱們不妨先來作個實驗感覺一下吧:
很明顯該實驗驗證了當 字段值長度大於64時,編碼格式會由 ZIPLIST方式切換爲 Hashtable方式。
源碼以前,了無祕密,咱們再來看一下Redis關於這部分切換的源碼實現,那就理解得更加清楚了:
下面詳解 OBJ_ENCODING_ZIPLIST
和 OBJ_ENCODING_HT
這兩種編碼格式的內部存儲模型,知道了其各自特色和優缺點,天然也就明白了Redis內部使用它們的意圖。
Ziplist 壓縮列表是一種緊湊編碼格式,整體思想是時間換空間,即以部分讀寫性能爲代價,來換取極高的內存空間利用率,所以只會用於 字段個數少,且字段值也較小 的場景。
壓縮列表內存利用率極高的緣由與其連續內存的特性是分不開的,其典型的內存結構能夠用下圖形象地展現出來:
因此若是用 Ziplist來存儲 Redis的散列類型的話,元素的排列方式就變成了以下圖所示的形象示意圖:即key和value都是邏輯連續內存:
OBJ_ENCODING_HT 這種編碼方式內部纔是真正的哈希表結構,或稱爲字典結構,其能夠實現O(1)複雜度的讀寫操做,所以效率很高。
在 Redis內部,從 OBJ_ENCODING_HT類型到底層真正的散列表數據結構是一層層嵌套下去的,關係以下:
這一關係咱們能夠從 Redis哈希表定義部分的源碼來看出:
下面來詳解一下各個部分:
Redis計算Hash的源代碼以下:
這是一個 C語言宏定義,其實幕後真正承擔 Hash值計算的是上面介紹的 dictType
結構體中的函數指針 hashFunction
。
而該 hashFunction
函數指針在初始化時會對應被賦值爲一個個真實的計算 Hash值的實際函數,就像下面這樣:
Index值的計算依賴於上面計算得出的 Hash值,代碼以下:
到此,還有一個一直很是值得關注的細節:即字典 dict里老是保存有兩個 Hash表結構 ht[2],以及與其高度相關的 rehash操做,這在下一篇文章裏詳解。
因爲能力有限,如有錯誤或者不當之處,還請你們批評指正,一塊兒學習交流!