1. 程式人生 > 其它 >9.hash雜湊雜湊(圖解)

9.hash雜湊雜湊(圖解)

Redis hash(雜湊雜湊)是由字元型別的 field(欄位)和 value 組成的雜湊對映表結構(也稱散列表),它非常類似於表格結構。在 hash 型別中,field 與 value 一一對應,且不允許重複。

Redis hash 特別適合於儲存物件。一個 filed/value 可以看做是表格中一條資料記錄;而一個 key 可以對應多條資料。下面舉一個例子,使用 hash 型別儲存表格中的資料,這裡以user為 key,id:1為欄位,name:Cao為 value:

使用者表格
id name
1 Cao
2 Zhao

命令例項演示:

#以user為key,設定 id+序號為欄位,name+名字為值
127.0.0.1:6379> HMSET user id:1 name:Cao id:2 name:Zhao
OK
# 查詢 user 這個key下所有的資料,並以字串的形式將值返回
127.0.0.1:6379> HGETALL user
1) "id:1"
2) "name:Cao"
3) "id:2"
4) "name:Zhao"

注意:當我們對 value 進行查詢時,這個值只能以字串的形式返回。

通過上述方法,我們就把表格中的資料儲存在了記憶體中。Redis hash 的儲存結構如下圖所示:


圖1:hash儲存結構圖


一個 hash 型別的 key 最多可以儲存 2^32-1(約 40 億個)欄位/值。同時 Redis hash 會為這個 key 額外儲存一些附加的管理資訊,比如這個鍵的型別、最後一次訪問這個鍵的時間等,所以 hash 鍵越來越多時,Redis 耗費在管理資訊方面的記憶體就越多。當 hash 型別移除最後一個元素後,該儲存結構就會被自動刪除,其佔用記憶體也會被系統回收。

一、初識hash型別

hash 型別是 Redis 常用資料型別之一,其底層儲存結構有兩種實現方式。

第一種,當儲存的資料量較少的時,hash 採用 ziplist 作為底層儲存結構,此時要求符合以下兩個條件:

  • 雜湊物件儲存的所有鍵值對(鍵和值)的字串長度總和小於 64 個位元組。
  • 雜湊物件儲存的鍵值對數量要小於 512 個。


當無法滿足上述條件時,hash 就會採用第二種方式來儲存資料,也就是 dict(字典結構),該結構類似於 Java 的 HashMap,是一個無序的字典,並採用了陣列和連結串列相結合的方式儲存資料。在 Redis 中,dict 是基於雜湊表演算法實現的,因此其查詢效能非常高效,其時間複雜度為 O(1)。

雜湊表又稱散列表,其初衷是將資料對映到陣列中的某個位置上,這樣就能夠通過陣列下標來訪問該資料,從而提高資料的查詢效率。下面通過一個示例,瞭解一下到底什麼是雜湊表。

現在有 1/5/8/ 三個數字,你需要把這三個數字對映到陣列中,由於雜湊表規定必須使用下標來訪問資料,因此你需要構建一個 0 到 8 的陣列,如下所示:



如上圖所示,我們把待查詢的數字,在相應的下標陣列上標記出來,它們之間一一對應。雖然這樣做能實現元素的查詢,但卻很浪費儲存空間,並且查詢效率也不高。而如果採用雜湊表的話,我們只需要申請一個長度為 3 的陣列(與待查詢的元素個數相同),如下圖所示:

將 1/5/8 分別對陣列長度 3 做取模運算,然後把它們指向運算結果對應的陣列槽位,這樣就把一組離散的資料對映到了連續的空間中,從而在最大限度上提高了空間的利用率,並且也提高了元素的查詢效率。但是你可能會發現一個問題,數字 5、8 竟然對映到同一個槽位上,這樣就導致其中一個數字無法查詢到。上述這種情況在實際中也會遇到,我們把它稱為“雜湊衝突”或者“雜湊碰撞”。

有許多方法可以解決“雜湊衝突”,比如開放地址法、連結串列地址法,再次雜湊法等,而 Redis 採用是連結串列地址法。這裡我們只對連結串列地址法做簡單介紹,很容易理解,這種方法就是將有衝突的資料使用連結串列把它們串聯起來,這樣即使發生了衝突,也可以將資料儲存在一起,最後,通過遍歷連結串列的方式就找到上述發生“衝突”的資料。如下所示:

如果值是字串的話,就需要通過雜湊函式將字串轉換成具體的數值,然後再對其進行對映。關於雜湊函式這裡不做過多介紹,如果感興趣可以自行研究。

二、常用命令彙總

hash常用命令彙總
命令 說明
HDEL key field2 [field2] 用於刪除一個或多個雜湊表字段。
HEXISTS key field 用於確定雜湊表字段是否存在。
HGET key field 獲取 key 關聯的雜湊欄位的值。
HGETALL key 獲取 key 關聯的所有雜湊欄位值。
HINCRBY key field increment 給 key 關聯的雜湊欄位做整數增量運算。
HINCRBYFLOAT key field increment 給 key 關聯的雜湊欄位做浮點數增量運算 。
HKEYS key 獲取key 關聯的所有欄位和值。
HLEN key 獲取 key 中的雜湊表的欄位數量。
HMSET key field1 value1 [field2 value2 ] 在雜湊表中同時設定多個 field-value(欄位-值)
HMGET key field1 [field2] 用於同時獲取多個給定雜湊欄位(field)對應的值。
HSET key field value 用於設定指定 key 的雜湊表字段和值(field/value)。
HSETNX key field value 僅當欄位 field 不存在時,設定雜湊表字段的值。
HVALS key 用於獲取雜湊表中的所有值。
HSCAN key cursor 迭代雜湊表中的所有鍵值對,cursor 表示遊標,預設為 0。

三、基本命令操作

示例演示:微博上好友關注時間的場景,這裡以使用者 ID 作為 key(user:10),field 欄位表示好友的 ID,value 則代表好友關注使用者(user:10)的時間。

#設定單個欄位
127.0.0.1:6379> HSET user:10 user:1 20201001
(integer) 1
#同時設定多個欄位
127.0.0.1:6379> HMSET user:10 user:2 20201002 user:3 20201004 user:4 20201018
OK
#查詢單個欄位
127.0.0.1:6379> HGET user:10 user:2
"20201002"
#查詢所有欄位
127.0.0.1:6379> HGETALL user:10
1) "user:1"
2) "20201001"
3) "user:2"
4) "20201002"
5) "user:3"
6) "20201004"
7) "user:4"
8) "20201018"
127.0.0.1:6379> HKEYS user:10
1) "user:1"
2) "user:2"
3) "user:3"
4) "user:4"
#返回欄位個數
127.0.0.1:6379> HLEN user:10
(integer) 4
#返回所有欄位值
127.0.0.1:6379> HVALS user:10
1) "20201001"
2) "20201002"
3) "20201004"
4) "20201018"
#迭代hash的key鍵
127.0.0.1:6379> HSCAN user:10 0
1) "0"
2) 1) "user:1"
   2) "20201001"
   3) "user:2"
   4) "20201002"
   5) "user:3"
   6) "20201004"
   7) "user:4"
   8) "20201018"
#判斷欄位是否存在,存在返回1,不存在返回0
127.0.0.1:6379> HEXISTS user:10 user:4
(integer) 1
127.0.0.1:6379> HEXISTS user:10 user:5
(integer) 0