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 採用是連結串列地址法。這裡我們只對連結串列地址法做簡單介紹,很容易理解,這種方法就是將有衝突的資料使用連結串列把它們串聯起來,這樣即使發生了衝突,也可以將資料儲存在一起,最後,通過遍歷連結串列的方式就找到上述發生“衝突”的資料。如下所示:
如果值是字串的話,就需要通過雜湊函式將字串轉換成具體的數值,然後再對其進行對映。關於雜湊函式這裡不做過多介紹,如果感興趣可以自行研究。
二、常用命令彙總
命令 | 說明 |
---|---|
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