Redis 非緩存核心場景及實例說明
一、分布式鎖
分布式鎖用于解決分布式系統中多節點競爭同一資源的問題,確保操作原子性。Redis 實現分布式鎖的核心思路是利用鍵的唯一性和原子命令,通常基于 Redisson 框架簡化實現(底層依賴 Redis 命令)。
實例:電商秒殺場景中防止庫存超賣。假設某商品庫存為 100 件,多臺應用服務器同時處理用戶搶購請求,需通過分布式鎖保證“查庫存-扣庫存”操作的原子性。
- 應用節點通過 Redisson 獲取鎖:
RLock lock = redisson.getLock("seckill:lock:goodsId123")
,底層會執行SET seckill:lock:goodsId123 value NX PX 30000
(NX 表示鍵不存在時才創建,PX 表示設置 30 秒過期時間,避免死鎖)。 - 獲取鎖成功后,查詢 Redis 中的商品庫存:
GET goods:stock:123
,若庫存 > 0,則執行扣庫存操作:DECR goods:stock:123
。 - 操作完成后釋放鎖:
lock.unlock()
,底層會刪除鎖鍵。若節點意外宕機,鎖鍵會因過期時間自動刪除,避免長期占用鎖資源。
二、限流
限流用于控制單位時間內接口的請求次數,防止系統因高并發過載。Redis 通常結合 Lua 腳本或 Redisson 實現,常見算法有令牌桶、漏桶等,Redisson 的 RRateLimiter
封裝了限流邏輯,底層依賴 Redis 數據結構和 Lua 腳本保證原子性。
實例:API 接口限流,限制單個用戶每分鐘最多發起 60 次請求(即每秒 1 次)。
- 使用 Redisson 創建限流器:
RRateLimiter limiter = redisson.getRateLimiter("api:limiter:userId456")
,設置限流規則:limiter.trySetRate(RateType.PER_USER, 60, 1, RateIntervalUnit.MINUTES)
(表示每個用戶每分鐘最多 60 次請求)。 - 每次用戶發起請求前,調用
boolean allowed = limiter.tryAcquire(1)
(嘗試獲取 1 個“令牌”)。若返回true
,則允許請求;若返回false
,則拒絕請求并返回“請求過于頻繁”。 - 底層通過 Redis 的
zset
存儲請求時間戳,結合 Lua 腳本計算單位時間內的請求次數,確保限流邏輯的原子性和準確性,避免多節點計數偏差。
三、消息隊列
Redis 可通過 List、Pub/Sub、Stream 三種方式實現消息隊列,其中 Stream 是 Redis 5.0 新增的專門用于消息隊列的數據結構,支持持久化、ACK 機制、消費組等特性,解決了 List 和 Pub/Sub 的消息丟失、堆積問題。
實例:訂單狀態變更通知,當訂單從“待支付”變為“已支付”時,需通知庫存系統扣減庫存、通知物流系統創建物流單。
- 生產者(訂單系統)向 Stream 發送消息:
XADD order:status:stream * orderId 789 status PAID
(*
表示由 Redis 生成唯一消息 ID,包含時間戳和序列號;orderId
和status
是消息內容)。 - 消費者組(庫存系統、物流系統)創建并訂閱 Stream:先創建消費組
XGROUP CREATE order:status:stream group:stock 0
(0
表示從最早的消息開始消費),再通過XREADGROUP GROUP group:stock consumer:stock 0 COUNT 1 BLOCK 0 STREAMS order:status:stream >
阻塞讀取消息(>
表示讀取未被消費的消息)。 - 消費者處理完消息后,發送 ACK 確認:
XACK order:status:stream group:stock 消息ID
,標記消息已處理,避免重復消費。若消費者宕機,未 ACK 的消息會留在“掛起隊列”,其他消費者可通過XPENDING
查詢并重新處理,保證消息不丟失。
四、延時隊列
延時隊列用于處理“延遲執行”的任務,如訂單超時未支付自動取消、紅包 24 小時未領取自動退還。Redis 實現延時隊列的主流方式是 Redisson 的 RDelayedQueue
,底層基于 zset
存儲任務(以“任務執行時間戳”為分數),結合定時任務掃描到期任務。
實例:訂單超時未支付自動取消,設置訂單創建后 30 分鐘未支付則取消。
- 創建 Redisson 客戶端并初始化延時隊列:
RBlockingQueue<String> blockingQueue = redisson.getBlockingQueue("order:delay:queue"); RDelayedQueue<String> delayedQueue = redisson.getDelayedQueue(blockingQueue);
。 - 訂單創建時,將訂單 ID 放入延時隊列并設置延遲時間:
delayedQueue.offer("orderId789", 30, TimeUnit.MINUTES)
(表示 30 分鐘后該訂單 ID 會被放入阻塞隊列)。 - 消費者(訂單處理系統)啟動線程,通過
String orderId = blockingQueue.take()
阻塞獲取到期的訂單 ID,然后執行取消邏輯(如更新訂單狀態為“已取消”、恢復商品庫存)。 - 底層通過
zset
存儲訂單 ID 和到期時間戳,Redisson 后臺線程定期掃描zset
,將分數(到期時間戳)小于當前時間的任務移動到阻塞隊列,實現延時觸發。同時,Redis 持久化機制(RDB/AOF)確保任務在 Redis 宕機后不丟失,重啟后可繼續執行。