Redis對象——哈希(Hash)

哈希在很多編程語言中都有著很廣泛的應用,而在Redis中也是如此,在redis中,哈希類型是指Redis鍵值對中的值本身又是一個鍵值對結構,形如 value=[{field1,value1},...{fieldN,valueN}] ,其與Redis字符串對象的區別如下圖所示:

Redis對象——哈希(Hash)

一、內部編碼

哈希類型的內部編碼有兩種: ziplist(壓縮列表) , hashtable(哈希表) 。只有當存儲的數據量比較小的情況下,Redis 才使用壓縮列表來實現字典類型。具體需要滿足兩個條件:

  • 當哈希類型元素個數小於hash-max-ziplist-entries配置(默認512個)
  • 所有值都小於hash-max-ziplist-value配置(默認64字節)ziplist 使用更加緊湊的結構實現多個元素的連續存儲,所以在節省內存方面比 hashtable 更加優秀。當哈希類型無法滿足 ziplist 的條件時,Redis會使用 hashtable 作為哈希的內部實現,因為此時 ziplist 的讀寫效率會下降,而 hashtable 的讀寫時間複雜度為O(1)。

二、常用命令

Redis哈希對象常用命令如下表(點擊命令可查看命令詳細說明)。

Redis哈希對象常用命令如下表(點擊命令可查看命令詳細說明)。

命令說明時間複雜度

HDEL key field [field ...]刪除一個或多個Hash的fieldO(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哈希對象常常用來緩存一些對象信息,如用戶信息、商品信息、配置信息等。

我們以用戶信息為例,它在關係型數據庫中的結構是這樣的

uidnameage1Tom152Jerry13

而使用Redis Hash存儲其結構如下圖:

Redis對象——哈希(Hash)

相比較於使用Redis字符串存儲,其有以下幾個優缺點:

  1. 原生字符串每個屬性一個鍵。set user:1:name Tom set user:1:age 15優點:簡單直觀,每個屬性都支持更新操作。缺點:佔用過多的鍵,內存佔用量較大,同時用戶信息內聚性比較差,所以此種方案一般不會在生產環境使用。
  2. 序列化字符串後,將用戶信息序列化後用一個鍵保存set user:1 serialize(userInfo)優點:簡化編程,如果合理的使用序列化可以提高內存的使用效率。缺點:序列化和反序列化有一定的開銷,同時每次更新屬性都需要把全部數據取出進行反序列化,更新後再序列化到Redis中。
  3. 序列化字符串後,將用戶信息序列化後用一個鍵保存hmset user:1 name Tom age 15優點:簡單直觀,如果使用合理可以減少內存空間的使用。缺點:要控制哈希在ziplist和hashtable兩種內部編碼的轉換,hashtable會消耗更多內存。

此外,我們曾經在做配置中心繫統的時候,使用Hash來緩存每個應用的配置信息,其在數據庫中的數據結構大致如下表

AppIdSettingKeySettingValue10001AppNamemyblog10001Version1.010002AppNameadmin site

在使用Redis Hash進行存儲的時候

新增或更新一個配置項

<code>127.0.0.1:6379> HSET 10001 AppName myblog
(integer) 1/<code>

獲取一個配置項

<code>127.0.0.1:6379> HGET 10001 AppName 
"myblog"/<code>

刪除一個配置項

<code>127.0.0.1:6379> HDEL 10001 AppName
(integer) 1/<code>

3.2 購物車

很多電商網站都會使用 cookie實現購物車,也就是將整個購物車都存儲到 cookie裡面。這種做法的一大 優點: 無須對數據庫進行寫入就可以實現購物車功能,這種方式大大提高了購物車的性能,而 缺點 則是程序需要重新解析和驗證( validate) cookie,確保cookie的格式正確,並且包含的商品都是真正可購買的商品。cookie購物車還有一個 缺點 :因為瀏覽器每次發送請求都會連 cookie一起發送,所以如果購物車cookie的體積比較大,那麼請求發送和處理的速度可能會有所降低。

購物車的定義非常簡單:我們以每個用戶的用戶ID(或者CookieId)作為Redis的Key,每個用戶的購物車都是一個哈希表,這個哈希表存儲了商品ID與商品訂購數量之間的映射。在商品的訂購數量出現變化時,我們操作Redis哈希對購物車進行更新:

如果用戶訂購某件商品的數量大於0,那麼程序會將這件商品的ID以及用戶訂購該商品的數量添加到散列裡面。

<code>//用戶1 商品1 數量1
127.0.0.1:6379> HSET uid:1 pid:1 1
(integer) 1 //返回值0代表改field在哈希表中不存在,為新增的field/<code>

如果用戶購買的商品已經存在於散列裡面,那麼新的訂購數量會覆蓋已有的訂購數量;

<code>//用戶1 商品1 數量5
127.0.0.1:6379> HSET uid:1 pid:1 5
(integer) 0 //返回值0代表改field在哈希表中已經存在/<code>

相反地,如果用戶訂購某件商品的數量不大於0,那麼程序將從散列裡面移除該條目。

<code>//用戶1 商品1
127.0.0.1:6379> HDEL uid:1 pid:2
(integer) 1/<code>

3.3 計數器

Redis 哈希表作為計數器的使用也非常廣泛。它常常被用在記錄網站每一天、一月、一年的訪問數量。每一次訪問,我們在對應的field上自增1

<code>//記錄我的
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/<code>

也經常被用在記錄商品的好評數量,差評數量上

<code>127.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/<code>

也可以實時記錄當天的在線的人數。

<code>//有人登陸
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/<code>

小結

本篇文章我們總結了Redis 哈希對象的內部實現、常用命令以及常用的一些場景,那麼大家在項目中對Redis哈希對象的使用都有哪些場景呢,歡迎在評論區給我留言和分享,我會第一時間反饋!我們共同學習與進步!

參考

《Redis設計與實現》

《Redis開發與運維》

《Redis官方文檔》


分享到:


相關文章: