美團面試:Redis 除了快取還能做什麼?可以做訊息佇列嗎?

JavaGuide發表於2023-11-02

這是一道面試中常見的 Redis 基礎面試題,主要考察求職者對於 Redis 應用場景的瞭解。

即使不準備面試也建議看看,實際開發中也能夠用到。

內容概覽:

Redis 除了做快取,還能做什麼?

  • 分散式鎖:透過 Redis 來做分散式鎖是一種比較常見的方式。通常情況下,我們都是基於 Redisson 來實現分散式鎖。關於 Redis 實現分散式鎖的詳細介紹,可以看我寫的這篇文章:如何基於 Redis 實現分散式鎖?
  • 限流:一般是透過 Redis + Lua 指令碼的方式來實現限流。相關閱讀:《我司用了 6 年的 Redis 分散式限流器,可以說是非常厲害了!》
  • 訊息佇列:Redis 自帶的 List 資料結構可以作為一個簡單的佇列使用。Redis 5.0 中增加的 Stream 型別的資料結構更加適合用來做訊息佇列。它比較類似於 Kafka,有主題和消費組的概念,支援訊息持久化以及 ACK 機制。
  • 延時佇列:Redisson 內建了延時佇列(基於 Sorted Set 實現的)。
  • 分散式 Session :利用 String 或者 Hash 資料型別儲存 Session 資料,所有的伺服器都可以訪問。
  • 複雜業務場景:透過 Redis 以及 Redis 擴充套件(比如 Redisson)提供的資料結構,我們可以很方便地完成很多複雜的業務場景比如透過 Bitmap 統計活躍使用者、透過 Sorted Set 維護排行榜。
  • ……

如何基於 Redis 實現分散式鎖?

關於 Redis 實現分散式鎖的詳細介紹,可以看我寫的這篇文章:如何基於 Redis 實現分散式鎖?

Redis 可以做訊息佇列麼?

實際專案中也沒見誰使用 Redis 來做訊息佇列,對於這部分知識點大家瞭解就好了。

先說結論:可以是可以,但不建議使用 Redis 來做訊息佇列。和專業的訊息佇列相比,還是有很多欠缺的地方。

Redis 2.0 之前,如果想要使用 Redis 來做訊息佇列的話,只能透過 List 來實現。

透過 RPUSH/LPOP 或者 LPUSH/RPOP即可實現簡易版訊息佇列:

# 生產者生產訊息
> RPUSH myList msg1 msg2
(integer) 2
> RPUSH myList msg3
(integer) 3
# 消費者消費訊息
> LPOP myList
"msg1"

不過,透過 RPUSH/LPOP 或者 LPUSH/RPOP這樣的方式存在效能問題,我們需要不斷輪詢去呼叫 RPOPLPOP 來消費訊息。當 List 為空時,大部分的輪詢的請求都是無效請求,這種方式大量浪費了系統資源。

因此,Redis 還提供了 BLPOPBRPOP 這種阻塞式讀取的命令(帶 B-Bloking 的都是阻塞式),並且還支援一個超時引數。如果 List 為空,Redis 服務端不會立刻返回結果,它會等待 List 中有新資料後在返回或者是等待最多一個超時時間後返回空。如果將超時時間設定為 0 時,即可無限等待,直到彈出訊息

# 超時時間為 10s
# 如果有資料立刻返回,否則最多等待10秒
> BRPOP myList 10
null

List 實現訊息佇列功能太簡單,像訊息確認機制等功能還需要我們自己實現,最要命的是沒有廣播機制,訊息也只能被消費一次。

Redis 2.0 引入了釋出訂閱 (pub/sub) 功能,解決了 List 實現訊息佇列沒有廣播機制的問題。

Redis 釋出訂閱 (pub/sub) 功能

pub/sub 中引入了一個概念叫 channel(頻道),釋出訂閱機制的實現就是基於這個 channel 來做的。

pub/sub 涉及釋出者(Publisher)和訂閱者(Subscriber,也叫消費者)兩個角色:

  • 釋出者透過 PUBLISH 投遞訊息給指定 channel。
  • 訂閱者透過SUBSCRIBE訂閱它關心的 channel。並且,訂閱者可以訂閱一個或者多個 channel。

我們這裡啟動 3 個 Redis 客戶端來簡單演示一下:

pub/sub 實現訊息佇列演示

pub/sub 既能單播又能廣播,還支援 channel 的簡單正則匹配。不過,訊息丟失(客戶端斷開連線或者 Redis 當機都會導致訊息丟失)、訊息堆積(釋出者釋出訊息的時候不會管消費者的具體消費能力如何)等問題依然沒有一個比較好的解決辦法。

為此,Redis 5.0 新增加的一個資料結構 Stream 來做訊息佇列。Stream 支援:

  • 釋出 / 訂閱模式
  • 按照消費者組進行消費
  • 訊息持久化( RDB 和 AOF)

Stream 使用起來相對要麻煩一些,這裡就不演示了。而且,Stream 在實際使用中依然會有一些小問題不太好解決比如在 Redis 發生故障恢復後不能保證訊息至少被消費一次。

綜上,和專業的訊息佇列相比,使用 Redis 來實現訊息佇列還是有很多欠缺的地方比如訊息丟失和堆積問題不好解決。因此,我們通常建議不要使用 Redis 來做訊息佇列,你完全可以選擇市面上比較成熟的一些訊息佇列比如 RocketMQ、Kafka。

相關閱讀:Redis 訊息佇列發展歷程 - 阿里開發者 - 2022

相關文章