Redis數據結構之散列
前言
在上一章《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
複雜度:
版本要求: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
複雜度:
版本要求: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)
複雜度:
版本要求: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會返回錯誤。
複雜度:
版本要求: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命令對應的減法操作命令,因此,可以通過傳入負數增量的方式來執行減法操作。
複雜度:
版本要求: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
複雜度:
版本要求: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
複雜度:
版本要求: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
複雜度:
版本要求:HDEL命令從Redis 2.0.0版本開始可用。
HLEN命令
HLEN命令可以獲取散列中包含的字段數量:
HLEN hash
127.0.0.1:6379> HLEN article::1
(integer) 4
如果給定的散列不存在,那麼返回0
複雜度:
版本要求: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命令一樣,如果指定的字段已存在於散列中,那麼就使用新值去覆蓋舊值。
複雜度:,其中爲被設置的字段數量。
版本要求: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)**作爲結果。
複雜度:,其中爲用戶給定的字段數量。
版本要求:HMGET命令從Redis 2.0.0版本開始可用。
HKEYS、HVALS、HGETALL命令
Redis爲散列提供了HKEYS、HVALS和HGETALL這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"
如果給定的散列並不存在,那麼HKEYS、HVALS和HGETALL都將返回一個空列表。
Redis散列包含的字段在底層是以無序的方式存儲的,根據字段插入的順序不同,包含相同字段的散列在執行HKEYS、HVALS、HGETALL命令時可能會得到不同的結果,因此在使用這3個命令時,不應該對它們的結果順序做任何假設。
複雜度:HKEYS命令、HVALS命令和HGETALL命令的複雜度都爲,其中爲散列包含的字段數量。
版本要求: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個方面對比字符串和散列鍵的優缺點:
結合上述對比結果,總結了一些選擇的條件和方法:
- 如果程序需要爲每個數據項單獨設置過期時間,那麼使用字符串鍵;
- 如果程序需要對數據項執行諸如SETRANGE、GETRANGE或者APPEND等操作,那麼優先考慮使用字符串鍵;
- 如果程序需要存儲的數據項比較多,並且你希望儘可能的減少內存佔用,優先使用散列鍵;
- 如果多個數據項在邏輯上屬於同一組或者同一類,那麼優先使用散列鍵;