1. 程式人生 > >《閒扯Redis六》Redis五種資料型別之Hash型

《閒扯Redis六》Redis五種資料型別之Hash型

------ ## 一、前言 >**Redis** 提供了5種資料型別:String(字串)、Hash(雜湊)、List(列表)、Set(集合)、Zset(有序集合),理解每種資料型別的特點對於redis的開發和運維非常重要。

原文解析

![Redis五種資料型別][1] **Redis** 中的 hash 是我們經常使用到的一種資料型別,根據使用方式的不同,可以應用到很多場景中。 ## 二、實現分析  由上述結構圖可知,Hash型別有以下兩種實現方式: >1、ziplist 編碼的雜湊物件使用壓縮列表作為底層實現 >2、hashtable 編碼的雜湊物件使用字典作為底層實現 ### 1.ziplist 編碼作為底層實現 ziplist 編碼的雜湊物件使用壓縮列表作為底層實現, 每當有新的鍵值對要加入到雜湊物件時, 程式會先將儲存了鍵的壓縮列表節點推入到壓縮列表表尾, 然後再將儲存了值的壓縮列表節點推入到壓縮列表表尾, 因此: >
儲存了同一鍵值對的兩個節點總是緊挨在一起, 儲存鍵的節點在前, 儲存值的節點在後; >先新增到雜湊物件中的鍵值對會被放在壓縮列表的表頭方向,而後來新增到雜湊物件中的鍵值對會被放在壓縮列表的表尾方向。 例如, 我們執行以下 HSET 命令, 那麼伺服器將建立一個列表物件作為 profile 鍵的值: ```java redis> HSET profile name "Tom" (integer) 1 redis> HSET profile age 25 (integer) 1 redis> HSET profile career "Programmer" (integer) 1 ``` profile 鍵的值物件使用的是 ziplist 編碼, 其中物件所使用的壓縮列表結構如下圖所示。
![Redis五種資料型別][2]
![Redis五種資料型別][3]
### 2.hashtable 編碼作為底層實現 hashtable 編碼的雜湊物件使用字典作為底層實現, 雜湊物件中的每個鍵值對都使用一個字典鍵值對來儲存: 字典的每個鍵都是一個字串物件, 物件中儲存了鍵值對的鍵; 字典的每個值都是一個字串物件, 物件中儲存了鍵值對的值。 例如, 如果前面 profile 鍵建立的不是 ziplist 編碼的雜湊物件, 而是 hashtable 編碼的雜湊物件, 那麼這個雜湊物件結構如下圖所示。
![Redis五種資料型別][4]
## 三、命令實現 因為雜湊鍵的值為雜湊物件, 所以用於雜湊鍵的所有命令都是針對雜湊物件來構建的, 下表列出了其中一部分雜湊鍵命令, 以及這些命令在不同編碼的雜湊物件下的實現方法。 |命令|ziplist 編碼實現方法|hashtable 編碼的實現方法| | -------- | :---- | :---- | |HSET |首先呼叫 ziplistPush 函式, 將鍵推入到壓縮列表的表尾, 然後再次呼叫 ziplistPush 函式, 將值推入到壓縮列表的表尾。 |呼叫 dictAdd 函式, 將新節點新增到字典裡面。| |HGET |首先呼叫 ziplistFind 函式, 在壓縮列表中查詢指定鍵所對應的節點, 然後呼叫 ziplistNext 函式, 將指標移動到鍵節點旁邊的值節點, 最後返回值節點。 |呼叫 dictFind 函式, 在字典中查詢給定鍵, 然後呼叫dictGetVal 函式, 返回該鍵所對應的值。| |HEXISTS |呼叫 ziplistFind 函式, 在壓縮列表中查詢指定鍵所對應的節點, 如果找到的話說明鍵值對存在, 沒找到的話就說明鍵值對不存在。 |呼叫 dictFind 函式, 在字典中查詢給定鍵, 如果找到的話說明鍵值對存在, 沒找到的話就說明鍵值對不存在。| |HDEL |呼叫 ziplistFind 函式, 在壓縮列表中查詢指定鍵所對應的節點, 然後將相應的鍵節點、 以及鍵節點旁邊的值節點都刪除掉。 |呼叫 dictDelete 函式, 將指定鍵所對應的鍵值對從字典中刪除掉。| |HLEN |呼叫 ziplistLen 函式, 取得壓縮列表包含節點的總數量, 將這個數量除以 2 , 得出的結果就是壓縮列表儲存的鍵值對的數量。 |呼叫 dictSize 函式, 返回字典包含的鍵值對數量, 這個數量就是雜湊物件包含的鍵值對數量。| |HGETALL |遍歷整個壓縮列表, 用 ziplistGet 函式返回所有鍵和值(都是節點)。 |遍歷整個字典, 用 dictGetKey 函式返回字典的鍵, 用dictGetVal 函式返回字典的值。| ## 四、編碼轉換 當雜湊物件可以同時滿足以下兩個條件時, 雜湊物件使用 ziplist 編碼: 雜湊物件儲存的所有鍵值對的鍵和值的字串長度都小於 64 位元組; 雜湊物件儲存的鍵值對數量小於 512 個; 不能滿足這兩個條件的雜湊物件需要使用 hashtable 編碼。 **注意**:這兩個條件的上限值是可以修改的, 具體請看配置檔案中關於 hash-max-ziplist-value 選項和 hash-max-ziplist-entries 選項的說明。 對於使用 ziplist 編碼的列表物件來說, 當使用 ziplist 編碼所需的兩個條件的任意一個不能被滿足時, 物件的編碼轉換操作就會被執行: 原本儲存在壓縮列表裡的所有鍵值對都會被轉移並儲存到字典裡面, 物件的編碼也會從 ziplist 變為 hashtable 。 以下程式碼展示了雜湊物件編碼轉換的情況: ### 1.鍵的長度太大引起編碼轉換 ```java # 雜湊物件只包含一個鍵和值都不超過 64 個位元組的鍵值對 redis>
HSET book name "Mastering C++ in 21 days" (integer) 1 redis> OBJECT ENCODING book "ziplist" # 向雜湊物件新增一個新的鍵值對,鍵的長度為 66 位元組 redis> HSET book long_long_long_long_long_long_long_long_long_long_long_description "content" (integer) 1 # 編碼已改變 redis> OBJECT ENCODING book "hashtable" ``` ### 2.值的長度太大引起編碼轉換 ```java # 雜湊物件只包含一個鍵和值都不超過 64 個位元組的鍵值對 redis> HSET blah greeting "hello world" (integer) 1 redis> OBJECT ENCODING blah "ziplist" # 向雜湊物件新增一個新的鍵值對,值的長度為 68 位元組 redis> HSET blah story "many string ... many string ... many string ... many string ... many" (integer) 1 # 編碼已改變 redis> OBJECT ENCODING blah "hashtable" ``` ### 3.鍵值對數量過多引起編碼轉換 ```java # 建立一個包含 512 個鍵值對的雜湊物件 redis> EVAL "for i=1, 512 do redis.call('HSET', KEYS[1], i, i) end" 1 "numbers" (nil) redis> HLEN numbers (integer) 512 redis> OBJECT ENCODING numbers "ziplist" # 再向雜湊物件新增一個新的鍵值對,使得鍵值對的數量變成 513 個 redis> HMSET numbers "key" "value" OK redis> HLEN numbers (integer) 513 # 編碼改變 redis> OBJECT ENCODING numbers "hashtable" ``` ## 五、要點總結 >1.Hash型別兩種編碼方式,ziplist 與 hashtable >2.hashtable 編碼的雜湊物件使用字典作為底層實現 >3.ziplist 與 hashtable 編碼方式之間存在轉換 ![大道七哥,有趣話不多][5] [1]: http://www.yund.tech/yund-cms//sys/common/view/files/20200402/list-2.png [2]: http://www.yund.tech/yund-cms//sys/common/view/files/20200402/redis6-1.png [3]: http://www.yund.tech/yund-cms//sys/common/view/files/20200402/redis6-2.png [4]: http://www.yund.tech/yund-cms//sys/common/view/files/20200402/redis6-3.png [5]: http://www.yund.tech/yund-cms//sys/common/view/files/20200618/gzhm