深刻剖析Redis系列(六) - Redis數據結構之哈希

前言

大部分編程語言都提供了 哈希hash)類型,它們的叫法多是 哈希字典關聯數組。在 Redis 中,哈希類型 是指鍵值自己又是一個 鍵值對結構java

哈希 形如 value={ {field1,value1},...{fieldN,valueN} }Redis 鍵值對哈希類型 兩者的關係如圖所示:mysql

哈希類型中的 映射關係 叫做 field-value,這裏的 value 是指 field 對應的 ,不是 對應的值。redis

其餘文章

正文

1. 相關命令

1.1. 基本命令

1.1.1. 設置值

hset key field value

下面爲 user:1 添加一對 field-value,若是設置成功會返回 1,反之會返回 0

127.0.0.1:6379> hset user:1 name tom
(integer) 1
複製代碼

此外 Redis 提供了 hsetnx 命令,它們的關係就像 setsetnx 命令同樣,只不過 做用域 變爲 field

1.1.2. 獲取值

hget key field

下面操做用於獲取 user:1name 域(屬性) 對應的值。

127.0.0.1:6379> hget user:1 name
"tom"
複製代碼

若是 field 不存在,會返回 nil

127.0.0.1:6379> hget user:2 name
(nil)
127.0.0.1:6379> hget user:1 age
(nil)
複製代碼

1.1.3. 刪除field

hdel key field [field ...]

hdel 會刪除 一個或多個 field,返回結果爲 成功刪除 field 的個數,例如:

127.0.0.1:6379> hdel user:1 name
(integer) 1
127.0.0.1:6379> hdel user:1 age
(integer) 0
複製代碼

1.1.4. 計算field個數

hlen key

例如鍵 user:13field

127.0.0.1:6379> hset user:1 name tom
(integer) 1
127.0.0.1:6379> hset user:1 age 23
(integer) 1
127.0.0.1:6379> hset user:1 city chengdu
(integer) 1
127.0.0.1:6379> hlen user:1
(integer) 3
複製代碼

1.1.5. 批量設置或獲取field-value

hmget key field [field ...] hmset key field value [field value ...]

hmsethmget 分別是 批量設置獲取 field-valuehmset 須要的參數是 key多對 field-valuehmget 須要的參數是 key多個 field。例如:

127.0.0.1:6379> hmset user:1 name tom age 12 city chengdu
OK
127.0.0.1:6379> hmget user:1 name city
1) "tom"
2) "chengdu"
複製代碼

1.1.6. 判斷field是否存在

hexists key field

例如 user:1 包含 name 域,因此返回結果爲 1,不包含時返回 0

127.0.0.1:6379> hexists user:1 name
(integer) 1
複製代碼

1.1.7. 獲取全部field

hkeys key

hkeys 命令應該叫 hfields 更爲恰當,它返回指定 哈希鍵 全部的 field,例如:

127.0.0.1:6379> hkeys user:1
1) "name"
2) "age"
3) "city"
複製代碼

1.1.8. 獲取全部value

hvals key

下面操做獲取 user:1 的所有 value

127.0.0.1:6379> hvals user:1
1) "tom"
2) "12"
3) "chengdu"
複製代碼

1.1.9. 獲取全部的field-value

hgetall key

下面操做獲取 user:1 全部的 field-value

127.0.0.1:6379> hgetall user:1
1) "name"
2) "tom"
3) "age"
4) "12"
5) "city"
6) "chengdu"
複製代碼

在使用 hgetall 時,若是 哈希元素 個數比較多,會存在 阻塞 Redis 的可能。若是開發人員只須要獲取 部分 field,可使用 hmget,若是必定要獲取 所有 field-value,可使用 hscan 命令,該命令會 漸進式遍歷 哈希類型。

1.2. 不經常使用命令

1.2.1. 鍵值自增

hincrby key field hincrbyfloat key field

hincrbyhincrbyfloat,就像 incrbyincrbyfloat 命令同樣,可是它們的 做用域field

1.2.2. 計算value的字符串長度

hstrlen key field

例如 hget user:1 namevaluetom,那麼 hstrlen 的返回結果是 3

127.0.0.1:6379> hstrlen user:1 name
(integer) 3
複製代碼

下面是 哈希類型命令時間複雜度,開發人員能夠參考此表選擇適合的命令。

2. 內部編碼

哈希類型內部編碼 有兩種:

2.1. ziplist(壓縮列表)

哈希類型 元素個數 小於 hash-max-ziplist-entries 配置(默認 512 個)、同時 全部值小於 hash-max-ziplist-value 配置(默認 64 字節)時,Redis 會使用 ziplist 做爲 哈希內部實現ziplist 使用更加 緊湊的結構 實現多個元素的 連續存儲,因此在 節省內存 方面比 hashtable 更加優秀。

2.2. hashtable(哈希表)

哈希類型 沒法知足 ziplist 的條件時,Redis 會使用 hashtable 做爲 哈希內部實現,由於此時 ziplist讀寫效率 會降低,而 hashtable 的讀寫 時間複雜度O(1)

下面的示例演示了 哈希類型內部編碼,以及相應的變化。

field 個數 比較少,且沒有大的 value 時,內部編碼ziplist

127.0.0.1:6379> hmset hashkey f1 v1 f2 v2
OK
127.0.0.1:6379> object encoding hashkey
"ziplist"
複製代碼
  • 當有 value 大於 64 字節時,內部編碼 會由 ziplist 變爲 hashtable
127.0.0.1:6379> hset hashkey f3 "one string is bigger than 64 byte...忽略..."
OK
127.0.0.1:6379> object encoding hashkey
"hashtable"
複製代碼
  • field 個數 超過 512內部編碼 也會由 ziplist 變爲 hashtable
127.0.0.1:6379> hmset hashkey f1 v1 f2 v2 f3 v3 ... f513 v513
OK
127.0.0.1:6379> object encoding hashkey
"hashtable"
複製代碼

3. 適用場景

如圖所示,爲 關係型數據表 的兩條 用戶信息,用戶的屬性做爲表的列,每條用戶信息做爲行。

使用 Redis 哈希結構 存儲 用戶信息 的示意圖以下:

相比於使用 字符串序列化 緩存 用戶信息哈希類型 變得更加 直觀,而且在 更新操做 上會 更加便捷。能夠將每一個用戶的 id 定義爲 鍵後綴,多對 field-value 對應每一個用戶的 屬性,相似以下僞代碼:

public UserInfo getUserInfo(long id) {
    // 用戶id做爲key後綴
    String userRedisKey = "user:info:" + id;
    // 使用hgetall獲取全部用戶信息映射關係
    Object userInfoMap = redis.hgetAll(userRedisKey);
    UserInfo userInfo;
    if (userInfoMap != null) {
        // 將映射關係轉換爲UserInfo
        userInfo = transferMapToUserInfo(userInfoMap);
    } else {
        // 從MySQL中獲取用戶信息
        userInfo = mysql.get(id);
        // 將userInfo變爲映射關係使用hmset保存到Redis中
        redis.hmset(userRedisKey, transferUserInfoToMap(userInfo));
        // 添加過時時間
        redis.expire(userRedisKey, 3600);
    }
    return userInfo;
}
複製代碼

3.1. 哈希結構與關係型表

須要注意的是 哈希類型關係型數據庫 有兩點不一樣之處:

  • 哈希類型稀疏的,而 關係型數據庫徹底結構化的,例如 哈希類型 每一個 能夠有不一樣的 field,而 關係型數據庫 一旦添加新的 全部行 都要爲其 設置值(即便爲 NULL),如圖所示:

  • 關係型數據庫 能夠作複雜的 關係查詢,而使用 Redis 去模擬關係型複雜查詢 開發困難維護成本高

3.2. 幾種緩存方式

到目前爲止,咱們已經可以用 三種方法 緩存 用戶信息,下面給出三種方案的 實現方法優缺點分析

3.2.1. 原生字符串類型

給用戶信息的每個屬性分配 一個鍵

set user:1:name tom
set user:1:age 23
set user:1:city beijing
複製代碼
  • 優勢:簡單直觀,每一個屬性都支持 更新操做

  • 缺點:佔用 過多的鍵內存佔用量 較大,同時用戶信息 內聚性比較差,因此此種方案通常不會在生產環境使用。

3.2.2. 序列化字符串類型

將用戶信息 序列化 後用 一個鍵 保存。

set user:1 serialize(userInfo)
複製代碼
  • 優勢簡化編程,若是合理的使用 序列化 能夠 提升內存利用率

  • 缺點序列化反序列化 有必定的開銷,同時每次 更新屬性 都須要把 所有數據 取出進行 反序列化更新後序列化Redis 中。

3.2.3. 哈希類型

每一個用戶屬性使用 一對 field-value,可是隻用 一個鍵 保存。

hmset user:1 name tom age 23 city beijing
複製代碼
  • 優勢簡單直觀,若是使用合理能夠 減小內存空間 的使用。

  • 缺點:要控制和減小 哈希ziplisthashtable 兩種 內部編碼轉換hashtable 會消耗 更多內存

小結

本文介紹了 Redis 中的 哈希結構 的 一些 基本命令內部編碼適用場景。最後對比了 關係型表哈希結構 的區別,以及幾種 存儲方式 的優缺點。

參考

《Redis 開發與運維》


歡迎關注技術公衆號: 零壹技術棧

零壹技術棧

本賬號將持續分享後端技術乾貨,包括虛擬機基礎,多線程編程,高性能框架,異步、緩存和消息中間件,分佈式和微服務,架構學習和進階等學習資料和文章。

相關文章
相關標籤/搜索