Redis學習手冊2—數據結構之散列

前言

在上一章《Redis學習手冊1—數據結構之字符串》中,我們學習了Redis最基本的數據結構——字符串,但是我們發現,在使用字符串類型鍵存儲具有關聯關係的一系列屬性時,我們不得不創建若干字符串鍵,分別存儲不同的屬性。例如,我們想要使用字符串描述一篇文章的屬性:標題、內容、作者和創建時間。如果使用字符串類型鍵存儲這些屬性,我們至少需要使用四個字符串類型鍵來存儲,然後定義相同的鍵的前綴,如:article::1::title、article::1::content、article::1::author、article::1::create_time。

雖然在技術上是可行的,但是要知道鍵本身也是數據,也是需要佔用內存資源的,那麼當數據量大的時候,就會造成不必要的內存浪費。爲了解決這個問題,使得相關聯的數據能夠被打包起來存儲,Redis提供了散列類型(hash) 鍵。

散列簡介

Redis的 散列(hash) 鍵會將一個鍵和一個散列在數據庫裏關聯起來,用戶可以在散列中爲任意多個 字段(field) 設置值。與字符串一樣,散列的字段和值既可以是文本數據,也可以是二進制數據。

通過使用散列鍵,用戶可以把相關聯的多項數據存儲到同一個散列裏,以便對這些數據進行管理,或者針對它們執行批量操作。

散列存儲結構

以文章數據爲例,下圖展示了散列鍵在內存中的存儲結構:
在這裏插入圖片描述

散列鍵的功能

Redis爲散列鍵提供了一系列操作命令,通過使用這些命令,用戶可以:

  • 爲散列的字段設置值,或者只在字段不存在的情況下爲它設置值;
  • 從散列裏面獲取給定字段名的值;
  • 對存儲着數字值的字段執行加法或減法操作;
  • 檢查給定的字段是否存在於散列中;
  • 從散列中刪除指定的字段;
  • 查看散列包含的字段數量;
  • 一次爲散列的多個字段設置值,或者一次從散列中獲取多個字段的值;
  • 獲取散列包含的所有字段、所有值或所有字段和值;

散列鍵命令速查表

以下表格中,列舉了散列鍵提供的所有命令及基本使用說明:

命令 用法及參數 說明
HSET HSET hash field value 爲散列中指定的字段設置值
HSETNX HSETNX hash field value 只在指定字段不存在時設置值
HGET HGET hash field 獲取指定字段的值
HINCRBY HINCRBY hash field increment 爲給定的字段值執行加法運算,與字符串鍵的INCRBY命令用法一致
HINCRBYFLOAT HINCRBYFLOAT hash field increment 支持浮點數字段值的加法操作
HSTRLEN HSTRLEN hash field 獲取給定字段值的字節長度
HEXISTS HEXISTS hash field 判斷給定的字段是否存在於散列中
HDEL HDEL hash field 刪除指定字段及其關聯的值
HMSET HMSET hash field value [field value ...] 一次爲多個字段設置值
HMGET HMGET hash field [field ...] 一次獲取多個字段的值
HKEYS HKEYS hash 獲取散列中包含的所有字段
HVALS HVALS hash 獲取散列中包含的所有值
HGETALL HGETALL hash 獲取散列中包含的所有字段和值

命令詳解

HSET命令:爲字段設置值

用戶可以通過執行HSET命令爲散列中指定的字段設置值:

HSET hash field value

根據給定的字段是否已經存在於散列中,HSET命令的行爲也會有所不同:

  • 如果給定的字段不存在於散列中,那麼這次設置就是一次創建操作,命令將在散列中關聯起給定的字段和值,然後返回 1
  • 如果給定的字段已經存在於散列中,那麼這次設置就是一次更新操作,命令將使用用戶給定的新值去覆蓋原來的值,然後返回 0
127.0.0.1:6379> HSET article::1 title "greeting"
(integer) 1
127.0.0.1:6379> HSET article::1 content "hello world"
(integer) 1
127.0.0.1:6379> HSET article::1 author "peter"
(integer) 1
127.0.0.1:6379> HSET article::1 create_time "2020-06-03 12:52:20"
(integer) 1
127.0.0.1:6379> HSET article::1 title "Redis Tutorial"
(integer) 0
127.0.0.1:6379> HSET article::1 content "Redis is a data structure store ..."
(integer) 0

複雜度:O(1)O(1)
版本要求:HSET命令從Redis 2.0.0版本開始可用。

HSETNX命令:只在字段不存在時爲它設置值

HSETNX命令的作用和HSET命令的作用非常相似,區別在於,HSETNX命令只會在指定字段不存在時執行設置操作:

HSETNX hash field value

HSETNX命令在字段不存在並且成功設置值時返回 1,在字段已存在並導致設置操作失敗時返回0

127.0.0.1:6379> HSETNX article::1 title "Redis Performance Test"
(integer) 0  -- 設置成功
127.0.0.1:6379> HSETNX article::1 view_count 100
(integer) 1

複雜度:O(1)O(1)
版本要求:HSETNX命令從Redis 2.0.0版本開始可用。

HGET命令:獲取字段的值

HGET命令可以根據用戶給定的字段,從散列中獲取該字段的值:

HGET hash field
127.0.0.1:6379> HGET article::1 title
"greeting"
127.0.0.1:6379> HGET article::1 author
"peter"

當給定的字段或散列不存在時,那麼HGET命令將返回一個空值:

127.0.0.1:6379> HGET account::54321 location
(nil)

複雜度:O(1)O(1)
版本要求:HGET命令從Redis 2.0.0版本可用。

HINCRBY命令

與字符串鍵的INCRBY命令一樣,如果散列的字段裏面存儲着能夠被Redis解釋爲整數的數字,那麼用戶就可以使用HINCRBY命令爲該字段的值上加上指定的整數增量

HINCRBY hash field increment

HINCRBY命令在成功執行加法操作之後將返回字段當前的值作爲命令的結果。

127.0.0.1:6379> HINCRBY article::1 view_count 1
(integer) 1

因爲Redis只爲散列提供了加法操作的HINCRBY命令,但是沒有爲散列提供相應的減法操作命令,因此可以使用傳入負數增量給HINCRBY命令,即可執行減法操作:

127.0.0.1:6379> HSET hash article::1 view_counte 100
(integer) 1
127.0.0.1:6379> HINCRBY hash article::1 view_counte 50
(integer) 50

HINCRBY命令只能作用於字段值爲整數,且增量也只能爲整數,否則Redis會返回錯誤。

複雜度:O(1)O(1)
版本要求:HNICRBY命令從Redis 2.0.0版本開始可用。

HINCRBYFLOAT命令

HINCRBYFLOAT命令和HINCRBY命令的作用類似,它們之間的主要區別在於HINCRBYFLOAT命令不僅可以使用整數作爲增量,還可以使用浮點數作爲增量:

HINCRBYFLOAT hash field increment

HINCRBYFLOAT命令在執行成功後,將返回給定字段的當前值作爲結果。

127.0.0.1:6379> HGET fruit::apple price
"5.60"
127.0.0.1:6379> HINCRBYFLOAT fruit::apple price 1.4
"7"

如果加法計算的結果能夠被表示爲整數,那麼HINCRBYFLOAT命令將使用整數作爲計算結果。

同樣的,Redis也沒有提供與HINCRBYFLOAT命令對應的減法操作命令,因此,可以通過傳入負數增量的方式來執行減法操作。

複雜度:O(1)O(1)
版本要求:HINCRBYFLOAT命令從Redis 2.0.0版本開始可用。

HSTRLEN命令

用戶可以使用HSTRLEN命令獲取給定字段值的字節長度:

HSTRLEN hash field
127.0.0.1:6379> HSET article::10086 title "hello world"
(integer) 1
127.0.0.1:6379> HSTRLEN article::10086 title
(integer) 11

如果給定的字段或散列不存在,那麼HSTRLEN命令將返回0

複雜度:O(1)O(1)
版本要求:HSTRLEN命令從Redis 3.2.0版本開始可用。

HEXISTS命令

HEXISTS命令可用於檢查用戶給定的字段是否存在於散列中:

HEXISTS hash field

如果散列中包含給定的字段,那麼命令返回1;如果給定的字段或散列不存在,則返回 0

127.0.0.1:6379> HEXISTS hash not-exists-field
(integer) 0
127.0.0.1:6379> HEXISTS not-exists-hash not-exists-field
(integer) 0
127.0.0.1:6379> HEXISTS exists-hash exists-field
(integer) 1

複雜度:O(1)O(1)
版本要求:HEXISTS命令從Redis 2.0.0版本開始可用。

HDEL命令

HDEL命令用於刪除散列中的指定字段及其相關聯的值:

HDEL hash field

HDEL命令刪除執行成功後返回1;如果給定的字段或給定的散列不存在,那麼返回 0

127.0.0.1:6379> HDEL article::1 author
(integer) 1
127.0.0.1:6379> HDEL not-exists-hash not-exists-field
(integer) 0

複雜度:O(1)O(1)
版本要求:HDEL命令從Redis 2.0.0版本開始可用。

HLEN命令

HLEN命令可以獲取散列中包含的字段數量:

HLEN hash
127.0.0.1:6379> HLEN article::1
(integer) 4

如果給定的散列不存在,那麼返回0

複雜度:O(1)O(1)
版本要求:HLEN命令從Redis 2.0.0版本開始可用。

HMSET命令

用戶可以使用HMSET命令一次爲散列中的多個字段設置值:

HMSET hash field value [field value]

HMSET命令在設置成功時返回OK。

127.0.0.1:6379> HMSET article::10086 title "greeting" content "hello world" author "peter" 
OK

如果使用HSET命令執行上述操作,那麼需要兩次才能完成,而使用HMSET命令卻只需要一次就可以,因此可以減少與Redis服務器的通信次數,提高執行速度。
HSET命令一樣,如果指定的字段已存在於散列中,那麼就使用新值去覆蓋舊值。

複雜度:O(N)O(N),其中NN爲被設置的字段數量。
版本要求:HMSET命令從Redis 2.0.0版本開始可用。

HMGET命令

HMSET命令相對應的,HMGET命令可以一次返回多個字段的值:

HMGET hash field [field ...]

HMGET命令將按照用戶給定的字段的順序依次返回與之對應的值。

127.0.0.1:6379> HMGET article::10086 title content
1) "greeting"
2) "hello world"

HGET命令一樣,如果給定的字段或散列不存在,那麼將返回空值 **(nil)**作爲結果。

複雜度:O(N)O(N),其中NN爲用戶給定的字段數量。
版本要求:HMGET命令從Redis 2.0.0版本開始可用。

HKEYS、HVALS、HGETALL命令

Redis爲散列提供了HKEYSHVALSHGETALL這3個命令,可以分別用於獲取散列包含的所有字段、所有值以及所有字段和值:

HKEYS hash
HVALS hash
HGETALL hash
127.0.0.1:6379> HKEYS article::10086
1) "title"
2) "content"
127.0.0.1:6379> HVALS article::10086
1) "greeting"
2) "hello world"
127.0.0.1:6379> HGETALL article::10086
1) "title"   -- 字段
2) "greeting"   -- 字段的值
3) "content"
4) "hello world"

如果給定的散列並不存在,那麼HKEYSHVALSHGETALL都將返回一個空列表。

Redis散列包含的字段在底層是以無序的方式存儲的,根據字段插入的順序不同,包含相同字段的散列在執行HKEYSHVALSHGETALL命令時可能會得到不同的結果,因此在使用這3個命令時,不應該對它們的結果順序做任何假設。

複雜度:HKEYS命令、HVALS命令和HGETALL命令的複雜度都爲O(N)O(N),其中NN爲散列包含的字段數量。

版本要求:HKEYS命令、HVALS命令和HGETALL命令都從Redis 2.0.0版本開始可用。

散列與字符串

類似的命令

以下表格展示了字符串命令與類似的散列命令:

字符串 散列
SET——爲一個字符串鍵設置值 HSET——爲散列的給定字段設置
SETNX——僅在字符串鍵不存在的時爲它設置值 HSETNX——僅在散列不包含指定字段時設置值
GET——獲取字符串的值 HGET——獲取散列指定字段的值
STRLEN——獲取字符串值的字節長度 HSTRLEN——獲取給定字段值的字節長度
INCRBY——對字符串鍵的值執行整數加法操作 HINCRBY——對字段存儲的數字值執行整數加法操作
INCRBYFLOAT——對字符串鍵存儲的浮點數執行浮點數加法操作 HINCRBYFLOAT——對字段存儲的數字值執行浮點數加法操作
MSET——一次爲多個字符串鍵設置值 HMSET——一次爲多個字段設置值
MGET——一次獲取多個字符串鍵的值 HMGET——一次獲取多個字段的值
EXISTS——檢查給定的鍵是否存在於數據庫中,可用於所有類型的鍵 HEXISTS——檢查給定的字段是否存在於散列中
DEL——從數據庫中刪除指定的鍵,適用於所有類型的鍵 HDEL——從散列中刪除指定的字段及它的值

散列鍵的優點

散列鍵的最大優點,就是它只要在數據庫中創建一個鍵,就可以把任意多個字段和值存儲到散列裏。相反,字符串鍵只能存儲一個鍵值對。

字符串鍵的優點

雖然使用散列鍵可以有效的節約資源並更好的組織數據,但是字符串鍵也有自己的優點:

  • 雖然散列鍵命令和字符串鍵命令在部分功能上有重合的地方,但是字符串鍵命令提供的操作比散列鍵命令更豐富。比如,字符串能夠使用SETRANGE命令和GETRANGE命令設置或讀取字符串值的部分內容,或者使用APPEND命令進行內容的追加,而散列鍵並不支持這些操作。
  • 鍵的過期,因爲Redis的鍵過期功能是針對整個鍵的,而散列鍵中包含的多個字段和值,卻不能爲各個字段設置不同的過期時間。

字符串鍵和散列鍵的選擇

從資源佔用、支持的操作及過期時間3個方面對比字符串和散列鍵的優缺點:
在這裏插入圖片描述
結合上述對比結果,總結了一些選擇的條件和方法:

  • 如果程序需要爲每個數據項單獨設置過期時間,那麼使用字符串鍵;
  • 如果程序需要對數據項執行諸如SETRANGEGETRANGE或者APPEND等操作,那麼優先考慮使用字符串鍵;
  • 如果程序需要存儲的數據項比較多,並且你希望儘可能的減少內存佔用,優先使用散列鍵;
  • 如果多個數據項在邏輯上屬於同一組或者同一類,那麼優先使用散列鍵;

上一篇:Redis學習手冊1—數據結構之字符串

下一篇:Redis學習手冊3—數據結構之列表

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章