好的,我們來詳細講解 Redis 的數據結構及其應用場景。Redis 的強大之處不僅僅在于它支持簡單的鍵值對,更在于它提供了豐富的數據結構,每種結構都針對特定類型的應用場景進行了優化。
核心數據結構與應用場景
Redis 主要支持以下五種核心數據結構:String(字符串)、Hash(哈希)、List(列表)、Set(集合)、Sorted Set(有序集合)。此外,還有 Bitmaps、HyperLogLogs、Streams 等更高級的結構。
1. String(字符串)
這是最簡單也是最基礎的數據類型。一個 Key 對應一個 Value。Value 不僅是字符串,也可以是數字(整數或浮點數),并且可以對數字進行自增/自減操作。
- 內部實現:基于簡單動態字符串(SDS)實現,可以修改的字符串,預分配內存,減少內存頻繁分配。
- 常用命令:
SET
,GET
,MSET
,MGET
,INCR
,DECR
,INCRBY
應用場景:
- 緩存:最經典的場景。將數據庫查詢結果、熱點數據、會話信息(Session)等序列化后存入 String,加快訪問速度。
SET user:1001 "{name: 'Alice', email: 'alice@example.com'}"
- 計數器:利用
INCR
、DECR
命令實現點贊數、瀏覽數、庫存計數等。這些操作是原子性的,非常適合高并發場景。INCR article:1001:views
- 分布式鎖:利用
SET key value NX EX seconds
命令(當 key 不存在時設置,并設置過期時間)可以實現簡單的分布式鎖。 - Session 共享:在集群服務中,將用戶的登錄會話信息集中存儲在 Redis 中,實現多臺服務器共享 Session。
2. Hash(哈希)
類似于 Java 中的 Map<String, Object>
,是一組鍵值對的集合。非常適合存儲對象。
- 內部實現:底層有兩種編碼方式:
ziplist
(壓縮列表,在元素少、體積小時使用)和hashtable
(哈希表)。 - 常用命令:
HSET
,HGET
,HMSET
,HMGET
,HGETALL
,HINCRBY
應用場景:
- 存儲對象:存儲用戶信息、商品信息等需要多個字段的對象。相比將整個對象序列化成 String,Hash 可以單獨獲取、修改某個字段,更節省網絡帶寬和存儲空間。
HSET user:1001 name "Alice" age "30" email "alice@example.com"
HGET user:1001 name
-> “Alice”
- 購物車:以用戶ID為 Key,商品ID為 Field,商品數量為 Value。
HSET cart:1001 product:5001 3
(用戶1001的商品5001數量為3)HINCRBY cart:1001 product:5001 1
(增加1件)
3. List(列表)
一個簡單的字符串列表,按插入順序排序。你可以從列表的頭部(左邊)或尾部(右邊)添加元素。
- 內部實現:底層是
quicklist
(快速列表),它是多個ziplist
通過雙向指針組成的鏈表,兼顧了空間效率和插入性能。 - 常用命令:
LPUSH
,RPUSH
,LPOP
,RPOP
,LRANGE
,BLPOP
(阻塞操作)
應用場景:
- 消息隊列:利用
LPUSH
+BRPOP
可以實現一個簡單的 FIFO(先進先出)隊列。生產者從左邊推入消息,消費者用阻塞方式從右邊取出消息。 - 最新列表:例如最新文章、最新評論、朋友圈時間線。利用
LPUSH
加入新元素,再用LRANGE 0 9
獲取最新的10條。LPUSH news:latest "News ID 10086"
- 記錄用戶操作歷史:例如用戶的最近搜索、最近瀏覽記錄。
4. Set(集合)
Redis 的 Set 是 String 類型的無序集合,集合內的元素是唯一的,不允許重復。
- 內部實現:底層是
intset
(整數集合,當元素都是整數且數量少時)或hashtable
(哈希表,value 為 null)。 - 常用命令:
SADD
,SMEMBERS
,SISMEMBER
,SINTER
(交集),SUNION
(并集),SDIFF
(差集)
應用場景:
- 標簽(Tag):給用戶、文章等對象打標簽。可以很方便地求交集、并集等。
SADD article:1001:tags "tech" "redis" "database"
SADD user:1002:tags "tech" "python"
SINTER article:1001:tags user:1002:tags
-> 獲取共同標簽 “tech”
- 共同關注/好友:利用
SINTER
可以輕松求出兩個用戶的共同好友。 - 抽獎/秒殺:利用
SADD
將所有參與用戶ID加入,可以自動保證唯一性,不會重復添加。SMEMBERS
可以列出所有參與者。 - 黑白名單:將需要過濾的 ID 放入 Set,用
SISMEMBER
快速判斷某個 ID 是否在名單內。
5. Sorted Set(有序集合 / ZSet)
與 Set 類似,也是 String 類型元素的集合,且不允許重復。但每個元素都會關聯一個 double
類型的分數(score)。Redis 正是通過分數來為集合中的成員進行從小到大的排序。成員是唯一的,但分數可以重復。
- 內部實現:底層是
ziplist
(壓縮列表)或skiplist
(跳躍表) +dict
(字典)的組合,跳躍表保證范圍查詢的效率,字典保證按成員查詢的效率。 - 常用命令:
ZADD
,ZRANGE
(按分數正序),ZREVRANGE
(按分數倒序),ZRANK
,ZREVRANK
,ZRANGEBYSCORE
應用場景:
- 排行榜:這是最經典的應用場景。將分數設為點擊量、銷量、熱度等,自動進行排序。
ZADD leaderboard 100 "player1" 200 "player2"
ZREVRANGE leaderboard 0 9 WITHSCORES
-> 獲取排行榜前十名
- 帶權重的隊列:分數可以作為優先級,實現優先級隊列。
- 范圍查找:例如處理成績表,快速查找分數在 [90, 100] 之間的學生。
ZRANGEBYSCORE grades 90 100
- 延時任務:將任務的執行時間作為 score,用一個進程輪詢獲取到期的任務(
ZRANGEBYSCORE key 0 <當前時間戳>
)。
其他高級數據結構
- Bitmaps(位圖): 本質上是 String,但可以對字符串的位進行操作。適用于大量布爾值的存儲,如用戶簽到記錄(每天1bit)、活躍用戶統計,極其節省空間。
- HyperLogLogs: 用于做基數統計(估算一個集合中不重復元素的個數),標準誤差僅0.81%。優點是非常節省空間。適用于統計網站的 UV(獨立訪客)、搜索關鍵詞的不重復數量等,
PFADD
,PFCOUNT
,PFMERGE
。 - Geospatial(地理空間): 可以存儲地理坐標,并計算距離、查找范圍內成員等。適用于附近的人、地理位置推薦。
- Streams(流): Redis 5.0 引入,專門為消息隊列設計,支持多消費者組、消息持久化、確認機制,功能比 List 更強大,是更專業的消息隊列解決方案。
總結與選擇建議
數據結構 | 特性 | 典型應用場景 |
---|---|---|
String | 簡單鍵值,支持數字和位操作 | 緩存、計數器、分布式鎖 |
Hash | 適合存儲對象,可部分更新 | 用戶信息、購物車、配置項 |
List | 有序、可重復,支持阻塞操作 | 消息隊列、最新列表、歷史記錄 |
Set | 無序、唯一,支持集合運算 | 標簽、共同好友、抽獎、黑白名單 |
Sorted Set | 唯一、有序(按分數排序) | 排行榜、優先級隊列、范圍查找 |
Bitmaps | 極省空間的布爾狀態存儲 | 用戶簽到、活躍用戶統計 |
HyperLogLog | 極省空間的基數估算 | 網站UV統計 |
Streams | 持久化的消息流 | 復雜消息隊列 |
選擇時考慮以下幾點:
- 數據形態:是需要一個對象(Hash)、一個列表(List)、一個不重復集合(Set)還是一個帶排序的集合(ZSet)?
- 操作類型:是需要頻繁讀取部分字段(Hash),還是需要排序(ZSet),或是需要集合運算(Set)?
- 性能與效率:String 存儲序列化對象雖然簡單,但修改一個字段就需要整個讀寫,不如 Hash 高效。在元素較少時,Redis 會使用更緊湊的編碼(如 ziplist)來節省內存。