【桌遊更新】《Marvel Zombies攻略》:#89—90
雜湊在很多程式語言中都有著很廣泛的應用,而在Redis中也是如此,在redis中,雜湊型別是指Redis鍵值對中的值本身又是一個鍵值對結構,形如value=[{field1,value1},...{fieldN,valueN}]
,其與Redis字串物件的區別如下圖所示:
一、內部編碼#
雜湊型別的內部編碼有兩種:ziplist(壓縮列表),hashtable(雜湊表)。只有當儲存的資料量比較小的情況下,Redis 才使用壓縮列表來實現字典型別。具體需要滿足兩個條件:
-
當雜湊型別元素個數小於hash-max-ziplist-entries配置(預設512個)
-
所有值都小於hash-max-ziplist-value配置(預設64位元組)
ziplist
hashtable
更加優秀。當雜湊型別無法滿足ziplist
的條件時,Redis會使用hashtable
作為雜湊的內部實現,因為此時ziplist
的讀寫效率會下降,而hashtable
的讀寫時間複雜度為O(1)。
有關ziplist和hashtable這兩種redis底層資料結構的具體實現可以參考我的另外兩篇文章。
二、常用命令#
Redis雜湊物件常用命令如下表(點選命令可檢視命令詳細說明)。
命令 | 說明 | 時間複雜度 |
---|---|---|
[HDEL key field field ...] |
刪除一個或多個Hash的field | O(N) N是被刪除的欄位數量。 |
HEXISTS key field | 判斷field是否存在於hash中 | O(1) |
HGET key field | 獲取hash中field的值 | O(1) |
HGETALL key | 從hash中讀取全部的域和值 | O(N) N是Hash的長度 |
HINCRBY key field increment | 將hash中指定域的值增加給定的數字 | O(1) |
HINCRBYFLOAT key field increment | 將hash中指定域的值增加給定的浮點數 | O(1) |
HKEYS key | 獲取hash的所有欄位 | O(N) N是Hash的長度 |
HLEN key | 獲取hash裡所有欄位的數量 | O(1) |
[HMGET key field field ...] | 獲取hash裡面指定欄位的值 | O(N) N是請求的欄位數 |
[HMSET key field value field value ...] | 設定hash欄位值 | O(N) N是設定的欄位數 |
HSET key field value | 設定hash裡面一個欄位的值 | O(1) |
HSETNX key field value | 設定hash的一個欄位,只有當這個欄位不存在時有效 | O(1) |
HSTRLEN key field | 獲取hash裡面指定field的長度 | O(1) |
HVALS key | 獲得hash的所有值 | O(N) N是Hash的長度 |
[HSCAN key cursor MATCH pattern] [COUNT count] | 迭代hash裡面的元素 |
三、適用場景#
3.1 儲存物件#
Redis雜湊物件常常用來快取一些物件資訊,如使用者資訊、商品資訊、配置資訊等。
我們以使用者資訊為例,它在關係型資料庫中的結構是這樣的
uid | name | age |
---|---|---|
1 | Tom | 15 |
2 | Jerry | 13 |
而使用Redis Hash儲存其結構如下圖:
相比較於使用Redis字串儲存,其有以下幾個優缺點:
-
原生字串每個屬性一個鍵。
Copyset user:1:name Tom set user:1:age 15
優點:簡單直觀,每個屬性都支援更新操作。
缺點:佔用過多的鍵,記憶體佔用量較大,同時使用者資訊內聚性比較差,所以此種方案一般不會在生產環境使用。 -
序列化字串後,將使用者資訊序列化後用一個鍵儲存
Copyset user:1 serialize(userInfo)
優點:簡化程式設計,如果合理的使用序列化可以提高記憶體的使用效率。
缺點:序列化和反序列化有一定的開銷,同時每次更新屬性都需要把全部資料取出進行反序列化,更新後再序列化到Redis中。 -
序列化字串後,將使用者資訊序列化後用一個鍵儲存
Copyhmset user:1 name Tom age 15
優點:簡單直觀,如果使用合理可以減少記憶體空間的使用。
缺點:要控制雜湊在ziplist和hashtable兩種內部編碼的轉換,hashtable會消耗更多記憶體。
此外,我們曾經在做配置中心繫統的時候,使用Hash來快取每個應用的配置資訊,其在資料庫中的資料結構大致如下表
AppId | SettingKey | SettingValue |
---|---|---|
10001 | AppName | myblog |
10001 | Version | 1.0 |
10002 | AppName | admin site |
在使用Redis Hash進行儲存的時候
新增或更新一個配置項
Copy127.0.0.1:6379> HSET 10001 AppName myblog
(integer) 1
獲取一個配置項
Copy127.0.0.1:6379> HGET 10001 AppName
"myblog"
刪除一個配置項
Copy127.0.0.1:6379> HDEL 10001 AppName
(integer) 1
3.2 購物車#
很多電商網站都會使用 cookie實現購物車,也就是將整個購物車都儲存到 cookie裡面。這種做法的一大優點:無須對資料庫進行寫入就可以實現購物車功能,這種方式大大提高了購物車的效能,而缺點則是程式需要重新解析和驗證( validate) cookie,確保cookie的格式正確,並且包含的商品都是真正可購買的商品。cookie購物車還有一個缺點:因為瀏覽器每次傳送請求都會連 cookie一起傳送,所以如果購物車cookie的體積比較大,那麼請求傳送和處理的速度可能會有所降低。
購物車的定義非常簡單:我們以每個使用者的使用者ID(或者CookieId)作為Redis的Key,每個使用者的購物車都是一個雜湊表,這個雜湊表儲存了商品ID與商品訂購數量之間的對映。在商品的訂購數量出現變化時,我們操作Redis雜湊對購物車進行更新:
如果使用者訂購某件商品的數量大於0,那麼程式會將這件商品的ID以及使用者訂購該商品的數量新增到雜湊裡面。
Copy//使用者1 商品1 數量1
127.0.0.1:6379> HSET uid:1 pid:1 1
(integer) 1 //返回值0代表改field在雜湊表中不存在,為新增的field
如果使用者購買的商品已經存在於雜湊裡面,那麼新的訂購數量會覆蓋已有的訂購數量;
Copy//使用者1 商品1 數量5
127.0.0.1:6379> HSET uid:1 pid:1 5
(integer) 0 //返回值0代表改field在雜湊表中已經存在
相反地,如果使用者訂購某件商品的數量不大於0,那麼程式將從雜湊裡面移除該條目。
Copy//使用者1 商品1
127.0.0.1:6379> HDEL uid:1 pid:2
(integer) 1
3.3 計數器#
Redis 雜湊表作為計數器的使用也非常廣泛。它常常被用在記錄網站每一天、一月、一年的訪問數量。每一次訪問,我們在對應的field上自增1
Copy//記錄我的
127.0.0.1:6379> HINCRBY MyBlog 202001 1
(integer) 1
127.0.0.1:6379> HINCRBY MyBlog 202001 1
(integer) 2
127.0.0.1:6379> HINCRBY MyBlog 202002 1
(integer) 1
127.0.0.1:6379> HINCRBY MyBlog 202002 1
(integer) 2
也經常被用在記錄商品的好評數量,差評數量上
Copy127.0.0.1:6379> HINCRBY pid:1 Good 1
(integer) 1
127.0.0.1:6379> HINCRBY pid:1 Good 1
(integer) 2
127.0.0.1:6379> HINCRBY pid:1 bad 1
(integer) 1
也可以實時記錄當天的線上的人數。
Copy//有人登陸
127.0.0.1:6379> HINCRBY MySite 20200310 1
(integer) 1
//有人登陸
127.0.0.1:6379> HINCRBY MySite 20200310 1
(integer) 2
//有人登出
127.0.0.1:6379> HINCRBY MySite 20200310 -1
(integer) 1
小結#
本篇文章我們總結了Redis 雜湊物件的內部實現、常用命令以及常用的一些場景,那麼大家在專案中對Redis雜湊物件的使用都有哪些場景呢,歡迎在評論區給我留言和分享,我會第一時間反饋!我們共同學習與進步!