Redis 5 種基本資料結構(String、List、Hash、Set、Sorted Set)詳解 | JavaGuide

語言: CN / TW / HK

Redis 5 種基本資料結構(String、List、Hash、Set、Sorted Set)在面試中經常會被問到,這篇文章我們一起來回顧溫習一下。

還有幾種比較特殊的資料結構(HyperLogLogs、Bitmap 、Geospatial、Stream)也非常重要,我們後面下次再聊!

下面是正文。

你可以在 Redis 官網上找到 Redis 資料結構非常詳細的介紹:

未來隨著 Redis 新版本的釋出,可能會有新的資料結構出現,通過查閱 Redis 官網對應的介紹,你總能獲取到最靠譜的資訊。

String(字串)

介紹

String 是 Redis 中最簡單同時也是最常用的一個數據結構。

String 是一種二進位制安全的資料結構,可以用來儲存任何型別的資料比如字串、整數、浮點數、圖片(圖片的 base64 編碼或者解碼或者圖片的路徑)、序列化後的物件。

雖然 Redis 是用 C 語言寫的,但是 Redis 並沒有使用 C 的字串表示,而是自己構建了一種 簡單動態字串 (Simple Dynamic String, SDS )。相比於 C 的原生字串,Redis 的 SDS 不光可以儲存文字資料還可以儲存二進位制資料,並且獲取字串長度複雜度為 O(1)(C 字串為 O(N)),除此之外,Redis 的 SDS API 是安全的,不會造成緩衝區溢位。

常用命令

命令 介紹
SET key value 設定指定 key 的值
SETNX key value 只有在 key 不存在時設定 key 的值
GET key 獲取指定 key 的值
MSET key1 value1 key2 value2 … 設定一個或多個指定 key 的值
MGET key1 key2 ... 獲取一個或多個指定 key 的值
STRLEN key 返回 key 所儲存的字串值的長度
INCR key 將 key 中儲存的數字值增一
DECR key 將 key 中儲存的數字值減一
EXISTS key 判斷指定 key 是否存在
DEL key(通用) 刪除指定的 key
EXPIRE key seconds(通用) 給指定 key 設定過期時間

更多 Redis String 命令以及詳細使用指南,請檢視 Redis 官網對應的介紹: http://redis.io/commands/?group=string

基本操作 :

> SET key value
OK
> GET key
"value"
> EXISTS key
(integer) 1
> STRLEN key
(integer) 5
> DEL key
(integer) 1
> GET key
(nil)

批量設定 :

> MSET key1 value1 key2 value2
OK
> MGET key1 key2 # 批量獲取多個 key 對應的 value
1) "value1"
2) "value2"

計數器(字串的內容為整數的時候可以使用):

> SET number 1
OK
> INCR number # 將 key 中儲存的數字值增一
(integer) 2
> GET number
"2"
> DECR number # 將 key 中儲存的數字值減一
(integer) 1
> GET number
"1"

設定過期時間(預設為永不過期):

> EXPIRE key 60
(integer) 1
> SETNX key 60 value # 設定值並設定過期時間
OK
> TTL key
(integer) 56

應用場景

需要儲存常規資料的場景

  • 舉例 :快取 session、token、圖片地址、序列化後的物件(相比較於 Hash 儲存更節省記憶體)。
  • 相關命令 : SETGET

需要計數的場景

  • 舉例 :使用者單位時間的請求數(簡單限流可以用到)、頁面單位時間的訪問數。
  • 相關命令 : SETGETINCRDECR

分散式鎖

利用 SETNX key value 命令可以實現一個最簡易的分散式鎖(存在一些缺陷,通常不建議這樣實現分散式鎖)。

List(列表)

介紹

Redis 中的 List 其實就是連結串列資料結構的實現。我在 線性資料結構 :陣列、連結串列、棧、佇列 這篇文章中詳細介紹了連結串列這種資料結構,我這裡就不多做介紹了。

許多高階程式語言都內建了連結串列的實現比如 Java 中的 LinkedList ,但是 C 語言並沒有實現連結串列,所以 Redis 實現了自己的連結串列資料結構。Redis 的 List 的實現為一個 雙向連結串列 ,即可以支援反向查詢和遍歷,更方便操作,不過帶來了部分額外的記憶體開銷。

常用命令

命令 介紹
RPUSH key value1 value2 ... 在指定列表的尾部(右邊)新增一個或多個元素
LPUSH key value1 value2 ... 在指定列表的頭部(左邊)新增一個或多個元素
LSET key index value 將指定列表索引 index 位置的值設定為 value
LPOP key 移除並獲取指定列表的第一個元素(最左邊)
RPOP key 移除並獲取指定列表的最後一個元素(最右邊)
LLEN key 獲取列表元素數量
LRANGE key start end 獲取列表 start 和 end 之間 的元素

更多 Redis List 命令以及詳細使用指南,請檢視 Redis 官網對應的介紹: http://redis.io/commands/?group=list

通過 RPUSH/LPOP 或者 LPUSH/RPOP實現佇列 :

> RPUSH myList value1
(integer) 1
> RPUSH myList value2 value3
(integer) 3
> LPOP myList
"value1"
> LRANGE myList 0 1
1) "value2"
2) "value3"
> LRANGE myList 0 -1
1) "value2"
2) "value3"

通過 RPUSH/RPOP或者LPUSH/LPOP 實現棧 :

> RPUSH myList2 value1 value2 value3
(integer) 3
> RPOP myList2 # 將 list的頭部(最右邊)元素取出
"value3"

我專門畫了一個圖方便大家理解 RPUSH , LPOP , lpush , RPOP 命令:

通過 LRANGE 檢視對應下標範圍的列表元素 :

> RPUSH myList value1 value2 value3
(integer) 3
> LRANGE myList 0 1
1) "value1"
2) "value2"
> LRANGE myList 0 -1
1) "value1"
2) "value2"
3) "value3"

通過 LRANGE 命令,你可以基於 List 實現分頁查詢,效能非常高!

通過 LLEN 檢視連結串列長度 :

> LLEN myList
(integer) 3

應用場景

資訊流展示

  • 舉例 :最新文章、最新動態。
  • 相關命令 : LPUSHLRANGE

訊息佇列

Redis List 資料結構可以用來做訊息佇列,只是功能過於簡單且存在很多缺陷,不建議這樣做。

相對來說,Redis 5.0 新增加的一個數據結構 Stream 更適合做訊息佇列一些,只是功能依然非常簡陋。和專業的訊息佇列相比,還是有很多欠缺的地方比如訊息丟失和堆積問題不好解決。

Hash(雜湊)

介紹

Redis 中的 Hash 是一個 String 型別的 field-value(鍵值對) 的對映表,特別適合用於儲存物件,後續操作的時候,你可以直接修改這個物件中的某些欄位的值。

Hash 類似於 JDK1.8 前的 HashMap ,內部實現也差不多(陣列 + 連結串列)。不過,Redis 的 Hash 做了更多優化。

常用命令

命令 介紹
HSET key field value 設定指定雜湊表中指定欄位的值
HSETNX key field value 只有指定欄位不存在時設定指定欄位的值
HMSET key field1 value1 field2 value2 ... 同時將一個或多個 field-value (域-值)對設定到指定雜湊表中
HGET key field 獲取指定雜湊表中指定欄位的值
HMGET key field1 field2 ... 獲取指定雜湊表中一個或者多個指定欄位的值
HGETALL key 獲取指定雜湊表中所有的鍵值對
HEXISTS key field 檢視指定雜湊表中指定的欄位是否存在
HDEL key field1 field2 ... 刪除一個或多個雜湊表字段
HLEN key 獲取指定雜湊表中欄位的數量

更多 Redis Hash 命令以及詳細使用指南,請檢視 Redis 官網對應的介紹: http://redis.io/commands/?group=hash

模擬物件資料儲存 :

> HMSET userInfoKey name "guide" description "dev" age "24"
OK
> HEXISTS userInfoKey name # 檢視 key 對應的 value中指定的欄位是否存在。
(integer) 1
> HGET userInfoKey name # 獲取儲存在雜湊表中指定欄位的值。
"guide"
> HGET userInfoKey age
"24"
> HGETALL userInfoKey # 獲取在雜湊表中指定 key 的所有欄位和值
1) "name"
2) "guide"
3) "description"
4) "dev"
5) "age"
6) "24"
> HSET userInfoKey name "GuideGeGe"
> HGET userInfoKey name
"GuideGeGe"

應用場景

物件資料儲存場景

  • 舉例 :使用者資訊、商品資訊、文章資訊、購物車資訊。
  • 相關命令 : HSET (設定單個欄位的值)、 HMSET (設定多個欄位的值)、 HGET (獲取單個欄位的值)、 HMGET (獲取多個欄位的值)。

Set(集合)

介紹

Redis 中的 Set 型別是一種無序集合,集合中的元素沒有先後順序但都唯一,有點類似於 Java 中的 HashSet 。當你需要儲存一個列表資料,又不希望出現重複資料時,Set 是一個很好的選擇,並且 Set 提供了判斷某個元素是否在一個 Set 集合內的重要介面,這個也是 List 所不能提供的。

你可以基於 Set 輕易實現交集、並集、差集的操作,比如你可以將一個使用者所有的關注人存在一個集合中,將其所有粉絲存在一個集合。這樣的話,Set 可以非常方便的實現如共同關注、共同粉絲、共同喜好等功能。這個過程也就是求交集的過程。

常用命令

命令 介紹
SADD key member1 member2 ... 向指定集合新增一個或多個元素
SMEMBERS key 獲取指定集合中的所有元素
SCARD key 獲取指定集合的元素數量
SISMEMBER key member 判斷指定元素是否在指定集合中
SINTER key1 key2 ... 獲取給定所有集合的交集
SINTERSTORE destination key1 key2 ... 將給定所有集合的交集儲存在 destination 中
SUNION key1 key2 ... 獲取給定所有集合的並集
SUNIONSTORE destination key1 key2 ... 將給定所有集合的並集儲存在 destination 中
SDIFF key1 key2 ... 獲取給定所有集合的差集
SDIFFSTORE destination key1 key2 ... 將給定所有集合的差集儲存在 destination 中
SPOP key count 隨機移除並獲取指定集合中一個或多個元素
SRANDMEMBER key count 隨機獲取指定集合中指定數量的元素

更多 Redis Set 命令以及詳細使用指南,請檢視 Redis 官網對應的介紹: http://redis.io/commands/?group=set

基本操作 :

> SADD mySet value1 value2
(integer) 2
> SADD mySet value1 # 不允許有重複元素,因此新增失敗
(integer) 0
> SMEMBERS mySet
1) "value1"
2) "value2"
> SCARD mySet
(integer) 2
> SISMEMBER mySet value1
(integer) 1
> SADD mySet2 value2 value3
(integer) 2
  • mySet : value1value2
  • mySet2value2value3

求交集 :

> SINTERSTORE mySet3 mySet mySet2
(integer) 1
> SMEMBERS mySet3
1) "value2"

求並集 :

> SUNION mySet mySet2
1) "value3"
2) "value2"
3) "value1"

求差集 :

> SDIFF mySet mySet2 # 差集是由所有屬於 mySet 但不屬於 A 的元素組成的集合
1) "value1"

應用場景

需要存放的資料不能重複的場景

HyperLogLog
SCARD

需要獲取多個數據源交集、並集和差集的場景

  • 舉例 :共同好友(交集)、共同粉絲(交集)、共同關注(交集)、好友推薦(差集)、音樂推薦(差集) 、訂閱號推薦(差集+交集) 等場景。
  • 相關命令: SINTER (交集)、 SINTERSTORE (交集)、 SUNION (並集)、 SUNIONSTORE (並集)、 SDIFF (交集)、 SDIFFSTORE (交集)。

需要隨機獲取資料來源中的元素的場景

  • 舉例 :抽獎系統、隨機。
  • 相關命令: SPOP (隨機獲取集合中的元素並移除,適合不允許重複中獎的場景)、 SRANDMEMBER (隨機獲取集合中的元素,適合允許重複中獎的場景)。

Sorted Set(有序集合)

介紹

Sorted Set 類似於 Set,但和 Set 相比,Sorted Set 增加了一個權重引數 score ,使得集合中的元素能夠按 score 進行有序排列,還可以通過 score 的範圍來獲取元素的列表。有點像是 Java 中 HashMapTreeSet 的結合體。

常用命令

命令 介紹
ZADD key score1 member1 score2 member2 ... 向指定有序集合新增一個或多個元素
ZCARD KEY 獲取指定有序集合的元素數量
ZSCORE key member 獲取指定有序集合中指定元素的 score 值
ZINTERSTORE destination numkeys key1 key2 ... 將給定所有有序集合的交集儲存在 destination 中,對相同元素對應的 score 值進行 SUM 聚合操作,numkeys 為集合數量
ZUNIONSTORE destination numkeys key1 key2 ... 求並集,其它和 ZINTERSTORE 類似
ZDIFF destination numkeys key1 key2 ... 求差集,其它和 ZINTERSTORE 類似
ZRANGE key start end 獲取指定有序集合 start 和 end 之間的元素(score 從低到高)
ZREVRANGE key start end 獲取指定有序集合 start 和 end 之間的元素(score 從高到底)
ZREVRANK key member 獲取指定有序集合中指定元素的排名(score 從大到小排序)

更多 Redis Sorted Set 命令以及詳細使用指南,請檢視 Redis 官網對應的介紹: http://redis.io/commands/?group=sorted-set

基本操作 :

> ZADD myZset 2.0 value1 1.0 value2
(integer) 2
> ZCARD myZset
2
> ZSCORE myZset value1
2.0
> ZRANGE myZset 0 1
1) "value2"
2) "value1"
> ZREVRANGE myZset 0 1
1) "value1"
2) "value2"
> ZADD myZset2 4.0 value2 3.0 value3
(integer) 2
  • myZset : value1 (2.0)、 value2 (1.0) 。
  • myZset2value2 (4.0)、 value3 (3.0) 。

獲取指定元素的排名 :

> ZREVRANK myZset value1
0
> ZREVRANK myZset value2
1

求交集 :

> ZINTERSTORE myZset3 2 myZset myZset2
1
> ZRANGE myZset3 0 1 WITHSCORES
value2
5

求並集 :

> ZUNIONSTORE myZset4 2 myZset myZset2
3
> ZRANGE myZset4 0 2 WITHSCORES
value1
2
value3
3
value2
5

求差集 :

> ZDIFF 2 myZset myZset2 WITHSCORES
value1
2

應用場景

需要隨機獲取資料來源中的元素根據某個權重進行排序的場景

  • 舉例 :各種排行榜比如直播間送禮物的排行榜、朋友圈的微信步數排行榜、王者榮耀中的段位排行榜、話題熱度排行榜等等。
  • 相關命令 : ZRANGE (從小到大排序) 、 ZREVRANGE (從大到小排序)、 ZREVRANK (指定元素排名)。

《Java 面試指北》 的「技術面試題篇」就有一篇文章詳細介紹如何使用 Sorted Set 來設計製作一個排行榜。

需要儲存的資料有優先順序或者重要程度的場景比如優先順序任務佇列。

  • 舉例 :優先順序任務佇列。
  • 相關命令 : ZRANGE (從小到大排序) 、 ZREVRANGE (從大到小排序)、 ZREVRANK (指定元素排名)。

參考

後記

專注 Java 原創乾貨分享,大三開源 JavaGuide (「Java學習+面試指南」一份涵蓋大部分 Java 程式設計師所需要掌握的核心知識。準備 Java 面試,首選 JavaGuide!),目前已經 120k+ Star。

原創不易,歡迎點贊分享, 歡迎關注我在部落格園的賬號 ,我會持續分享原創乾貨!加油,衝!

如果本文對你有幫助的話,歡迎點贊分享,這對我繼續分享&創作優質文章非常重要。感謝 :pray|type_1_2: