Redis 究竟适不适合当队列来用?
?
作者 |?Magic Kaito
來源 | 水滴與銀彈
我經常聽到很多人討論,關于「把 Redis 當作隊列來用是否合適」的問題。
有些人表示贊成,他們認為 Redis 很輕量,用作隊列很方便。
也些人則反對,認為 Redis 會「丟」數據,最好還是用「專業」的隊列中間件更穩妥。
究竟哪種方案更好呢?
這篇文章,我就和你聊一聊把 Redis 當作隊列,究竟是否合適這個問題。
我會從簡單到復雜,一步步帶你梳理其中的細節,把這個問題真正的講清楚。
看完這篇文章后,我希望你對這個問題你會有全新的認識。
從最簡單的開始:List 隊列
首先,我們先從最簡單的場景開始講起。
如果你的業務需求足夠簡單,想把 Redis 當作隊列來使用,肯定最先想到的就是使用 List 這個數據類型。
因為 List 底層的實現就是一個「鏈表」,在頭部和尾部操作元素,時間復雜度都是 O(1),這意味著它非常符合消息隊列的模型。
如果把 List 當作隊列,你可以這么來用。
生產者使用 LPUSH 發布消息:
127.0.0.1:6379>?LPUSH?queue?msg1 (integer)?1 127.0.0.1:6379>?LPUSH?queue?msg2 (integer)?2消費者這一側,使用 RPOP 拉取消息:
127.0.0.1:6379>?RPOP?queue "msg1" 127.0.0.1:6379>?RPOP?queue "msg2"這個模型非常簡單,也很容易理解。
但這里有個小問題,當隊列中已經沒有消息了,消費者在執行 RPOP 時,會返回 NULL。
127.0.0.1:6379>?RPOP?queue (nil)???//?沒消息了而我們在編寫消費者邏輯時,一般是一個「死循環」,這個邏輯需要不斷地從隊列中拉取消息進行處理,偽代碼一般會這么寫:
while?true:msg?=?redis.rpop("queue")//?沒有消息,繼續循環if?msg?==?null:continue//?處理消息handle(msg)如果此時隊列為空,那消費者依舊會頻繁拉取消息,這會造成「CPU 空轉」,不僅浪費 CPU 資源,還會對 Redis 造成壓力。
怎么解決這個問題呢?
也很簡單,當隊列為空時,我們可以「休眠」一會,再去嘗試拉取消息。代碼可以修改成這樣:
while?true:msg?=?redis.rpop("queue")//?沒有消息,休眠2sif?msg?==?null:sleep(2)continue//?處理消息????????handle(msg)這就解決了 CPU 空轉問題。
這個問題雖然解決了,但又帶來另外一個問題:當消費者在休眠等待時,有新消息來了,那消費者處理新消息就會存在「延遲」。
假設設置的休眠時間是 2s,那新消息最多存在 2s 的延遲。
要想縮短這個延遲,只能減小休眠的時間。但休眠時間越小,又有可能引發 CPU 空轉問題。
魚和熊掌不可兼得。
那如何做,既能及時處理新消息,還能避免 CPU 空轉呢?
Redis 是否存在這樣一種機制:如果隊列為空,消費者在拉取消息時就「阻塞等待」,一旦有新消息過來,就通知我的消費者立即處理新消息呢?
幸運的是,Redis 確實提供了「阻塞式」拉取消息的命令:BRPOP / BLPOP,這里的 B 指的是阻塞(Block)。
現在,你可以這樣來拉取消息了:
while?true://?沒消息阻塞等待,0表示不設置超時時間msg?=?redis.brpop("queue",?0)if?msg?==?null:continue//?處理消息handle(msg)使用 BRPOP 這種阻塞式方式拉取消息時,還支持傳入一個「超時時間」,如果設置為 0,則表示不設置超時,直到有新消息才返回,否則會在指定的超時時間后返回 NULL。
這個方案不錯,既兼顧了效率,還避免了 CPU 空轉問題,一舉兩得。
注意:如果設置的超時時間太長,這個連接太久沒有活躍過,可能會被 Redis Server 判定為無效連接,之后 Redis Server 會強制把這個客戶端踢下線。所以,采用這種方案,客戶端要有重連機制。
解決了消息處理不及時的問題,你可以再思考一下,這種隊列模型,有什么缺點?
我們一起來分析一下:
不支持重復消費:消費者拉取消息后,這條消息就從 List 中刪除了,無法被其它消費者再次消費,即不支持多個消費者消費同一批數據
消息丟失:消費者拉取到消息后,如果發生異常宕機,那這條消息就丟失了
第一個問題是功能上的,使用 List 做消息隊列,它僅僅支持最簡單的,一組生產者對應一組消費者,不能滿足多組生產者和消費者的業務場景。
第二個問題就比較棘手了,因為從 List 中 POP 一條消息出來后,這條消息就會立即從鏈表中刪除了。也就是說,無論消費者是否處理成功,這條消息都沒辦法再次消費了。
這也意味著,如果消費者在處理消息時異常宕機,那這條消息就相當于丟失了。
針對這 2 個問題怎么解決呢?我們一個個來看。
發布/訂閱模型:Pub/Sub
從名字就能看出來,這個模塊是 Redis 專門是針對「發布/訂閱」這種隊列模型設計的。
它正好可以解決前面提到的第一個問題:重復消費。
即多組生產者、消費者的場景,我們來看它是如何做的。
Redis 提供了 PUBLISH / SUBSCRIBE 命令,來完成發布、訂閱的操作。
假設你想開啟 2 個消費者,同時消費同一批數據,就可以按照以下方式來實現。
首先,使用 SUBSCRIBE 命令,啟動 2 個消費者,并「訂閱」同一個隊列。
//?2個消費者?都訂閱一個隊列 127.0.0.1:6379>?SUBSCRIBE?queue Reading?messages...?(press?Ctrl-C?to?quit) 1)?"subscribe" 2)?"queue" 3)?(integer)?1此時,2 個消費者都會被阻塞住,等待新消息的到來。
之后,再啟動一個生產者,發布一條消息。
127.0.0.1:6379>?PUBLISH?queue?msg1 (integer)?1這時,2 個消費者就會解除阻塞,收到生產者發來的新消息。
127.0.0.1:6379>?SUBSCRIBE?queue //?收到新消息 1)?"message" 2)?"queue" 3)?"msg1"看到了么,使用 Pub/Sub 這種方案,既支持阻塞式拉取消息,還很好地滿足了多組消費者,消費同一批數據的業務需求。
除此之外,Pub/Sub 還提供了「匹配訂閱」模式,允許消費者根據一定規則,訂閱「多個」自己感興趣的隊列。
//?訂閱符合規則的隊列 127.0.0.1:6379>?PSUBSCRIBE?queue.* Reading?messages...?(press?Ctrl-C?to?quit) 1)?"psubscribe" 2)?"queue.*" 3)?(integer)?1這里的消費者,訂閱了 queue.* 相關的隊列消息。
之后,生產者分別向 queue.p1 和 queue.p2 發布消息。
127.0.0.1:6379>?PUBLISH?queue.p1?msg1 (integer)?1 127.0.0.1:6379>?PUBLISH?queue.p2?msg2 (integer)?1這時再看消費者,它就可以接收到這 2 個生產者的消息了。
127.0.0.1:6379>?PSUBSCRIBE?queue.* Reading?messages...?(press?Ctrl-C?to?quit) ... //?來自queue.p1的消息 1)?"pmessage" 2)?"queue.*" 3)?"queue.p1" 4)?"msg1"//?來自queue.p2的消息 1)?"pmessage" 2)?"queue.*" 3)?"queue.p2" 4)?"msg2"我們可以看到,Pub/Sub 最大的優勢就是,支持多組生產者、消費者處理消息。
講完了它的優點,那它有什么缺點呢?
其實,Pub/Sub 最大問題是:丟數據。
如果發生以下場景,就有可能導致數據丟失:
消費者下線
Redis 宕機
消息堆積
究竟是怎么回事?
這其實與 Pub/Sub 的實現方式有很大關系。
Pub/Sub 在實現時非常簡單,它沒有基于任何數據類型,也沒有做任何的數據存儲,它只是單純地為生產者、消費者建立「數據轉發通道」,把符合規則的數據,從一端轉發到另一端。
一個完整的發布、訂閱消息處理流程是這樣的:
消費者訂閱指定隊列,Redis 就會記錄一個映射關系:隊列->消費者
生產者向這個隊列發布消息,那 Redis 就從映射關系中找出對應的消費者,把消息轉發給它
看到了么,整個過程中,沒有任何的數據存儲,一切都是實時轉發的。
這種設計方案,就導致了上面提到的那些問題。
例如,如果一個消費者異常掛掉了,它再重新上線后,只能接收新的消息,在下線期間生產者發布的消息,因為找不到消費者,都會被丟棄掉。
如果所有消費者都下線了,那生產者發布的消息,因為找不到任何一個消費者,也會全部「丟棄」。
所以,當你在使用 Pub/Sub 時,一定要注意:消費者必須先訂閱隊列,生產者才能發布消息,否則消息會丟失。
這也是前面講例子時,我們讓消費者先訂閱隊列,之后才讓生產者發布消息的原因。
另外,因為 Pub/Sub 沒有基于任何數據類型實現,所以它也不具備「數據持久化」的能力。
也就是說,Pub/Sub 的相關操作,不會寫入到 RDB 和 AOF 中,當 Redis 宕機重啟,Pub/Sub 的數據也會全部丟失。
最后,我們來看 Pub/Sub 在處理「消息積壓」時,為什么也會丟數據?
當消費者的速度,跟不上生產者時,就會導致數據積壓的情況發生。
如果采用 List 當作隊列,消息積壓時,會導致這個鏈表很長,最直接的影響就是,Redis 內存會持續增長,直到消費者把所有數據都從鏈表中取出。
但 Pub/Sub 的處理方式卻不一樣,當消息積壓時,有可能會導致消費失敗和消息丟失!
這是怎么回事?
還是回到 Pub/Sub 的實現細節上來說。
每個消費者訂閱一個隊列時,Redis 都會在 Server 上給這個消費者在分配一個「緩沖區」,這個緩沖區其實就是一塊內存。
當生產者發布消息時,Redis 先把消息寫到對應消費者的緩沖區中。
之后,消費者不斷地從緩沖區讀取消息,處理消息。
但是,問題就出在這個緩沖區上。
因為這個緩沖區其實是有「上限」的(可配置),如果消費者拉取消息很慢,就會造成生產者發布到緩沖區的消息開始積壓,緩沖區內存持續增長。
如果超過了緩沖區配置的上限,此時,Redis 就會「強制」把這個消費者踢下線。
這時消費者就會消費失敗,也會丟失數據。
如果你有看過 Redis 的配置文件,可以看到這個緩沖區的默認配置:client-output-buffer-limit pubsub 32mb 8mb 60。
它的參數含義如下:
32mb:緩沖區一旦超過 32MB,Redis 直接強制把消費者踢下線
8mb + 60:緩沖區超過 8MB,并且持續 60 秒,Redis 也會把消費者踢下線
Pub/Sub 的這一點特點,是與 List 作隊列差異比較大的。
從這里你應該可以看出,List 其實是屬于「拉」模型,而 Pub/Sub 其實屬于「推」模型。
List 中的數據可以一直積壓在內存中,消費者什么時候來「拉」都可以。
但 Pub/Sub 是把消息先「推」到消費者在 Redis Server 上的緩沖區中,然后等消費者再來取。
當生產、消費速度不匹配時,就會導致緩沖區的內存開始膨脹,Redis 為了控制緩沖區的上限,所以就有了上面講到的,強制把消費者踢下線的機制。
好了,現在我們總結一下 Pub/Sub 的優缺點:
支持發布 / 訂閱,支持多組生產者、消費者處理消息
消費者下線,數據會丟失
不支持數據持久化,Redis 宕機,數據也會丟失
消息堆積,緩沖區溢出,消費者會被強制踢下線,數據也會丟失
有沒有發現,除了第一個是優點之外,剩下的都是缺點。
所以,很多人看到 Pub/Sub 的特點后,覺得這個功能很「雞肋」。
也正是以上原因,Pub/Sub 在實際的應用場景中用得并不多。
目前只有哨兵集群和 Redis 實例通信時,采用了 Pub/Sub 的方案,因為哨兵正好符合即時通訊的業務場景。
我們再來看一下,Pub/Sub 有沒有解決,消息處理時異常宕機,無法再次消費的問題呢?
其實也不行,Pub/Sub 從緩沖區取走數據之后,數據就從 Redis 緩沖區刪除了,消費者發生異常,自然也無法再次重新消費。
好,現在我們重新梳理一下,我們在使用消息隊列時的需求。
當我們在使用一個消息隊列時,希望它的功能如下:
支持阻塞等待拉取消息
支持發布 / 訂閱模式
消費失敗,可重新消費,消息不丟失
實例宕機,消息不丟失,數據可持久化
消息可堆積
Redis 除了 List 和 Pub/Sub 之外,還有符合這些要求的數據類型嗎?
其實,Redis 的作者也看到了以上這些問題,也一直在朝著這些方向努力著。
Redis 作者在開發 Redis 期間,還另外開發了一個開源項目 disque。
這個項目的定位,就是一個基于內存的分布式消息隊列中間件。
但由于種種原因,這個項目一直不溫不火。
終于,在 Redis 5.0 版本,作者把 disque 功能移植到了 Redis 中,并給它定義了一個新的數據類型:Stream。
下面我們就來看看,它能符合上面提到的這些要求嗎?
趨于成熟的隊列:Stream
我們來看 Stream 是如何解決上面這些問題的。
我們依舊從簡單到復雜,依次來看 Stream 在做消息隊列時,是如何處理的?
首先,Stream 通過 XADD 和 XREAD 完成最簡單的生產、消費模型:
XADD:發布消息
XREAD:讀取消息
生產者發布 2 條消息:
//?*表示讓Redis自動生成消息ID 127.0.0.1:6379>?XADD?queue?*?name?zhangsan "1618469123380-0" 127.0.0.1:6379>?XADD?queue?*?name?lisi "1618469127777-0"使用 XADD 命令發布消息,其中的「*」表示讓 Redis 自動生成唯一的消息 ID。
這個消息 ID 的格式是「時間戳-自增序號」。
消費者拉取消息:
//?從開頭讀取5條消息,0-0表示從開頭讀取 127.0.0.1:6379>?XREAD?COUNT?5?STREAMS?queue?0-0 1)?1)?"queue"2)?1)?1)?"1618469123380-0"2)?1)?"name"2)?"zhangsan"2)?1)?"1618469127777-0"2)?1)?"name"2)?"lisi"如果想繼續拉取消息,需要傳入上一條消息的 ID:
127.0.0.1:6379>?XREAD?COUNT?5?STREAMS?queue?1618469127777-0 (nil)沒有消息,Redis 會返回 NULL。
以上就是 Stream 最簡單的生產、消費。
這里不再重點介紹 Stream 命令的各種參數,我在例子中演示時,凡是大寫的單詞都是「固定」參數,凡是小寫的單詞,都是可以自己定義的,例如隊列名、消息長度等等,下面的例子規則也是一樣,為了方便你理解,這里有必要提醒一下。
下面我們來看,針對前面提到的消息隊列要求,Stream 都是如何解決的?
1) Stream 是否支持「阻塞式」拉取消息?
可以的,在讀取消息時,只需要增加 BLOCK 參數即可。
//?BLOCK?0?表示阻塞等待,不設置超時時間 127.0.0.1:6379>?XREAD?COUNT?5?BLOCK?0?STREAMS?queue?1618469127777-0這時,消費者就會阻塞等待,直到生產者發布新的消息才會返回。
2) Stream 是否支持發布 / 訂閱模式?
也沒問題,Stream 通過以下命令完成發布訂閱:
XGROUP:創建消費者組
XREADGROUP:在指定消費組下,開啟消費者拉取消息
下面我們來看具體如何做?
首先,生產者依舊發布 2 條消息:
127.0.0.1:6379>?XADD?queue?*?name?zhangsan "1618470740565-0" 127.0.0.1:6379>?XADD?queue?*?name?lisi "1618470743793-0"之后,我們想要開啟 2 組消費者處理同一批數據,就需要創建 2 個消費者組:
//?創建消費者組1,0-0表示從頭拉取消息 127.0.0.1:6379>?XGROUP?CREATE?queue?group1?0-0 OK //?創建消費者組2,0-0表示從頭拉取消息 127.0.0.1:6379>?XGROUP?CREATE?queue?group2?0-0 OK消費者組創建好之后,我們可以給每個「消費者組」下面掛一個「消費者」,讓它們分別處理同一批數據。
第一個消費組開始消費:
//?group1的consumer開始消費,>表示拉取最新數據 127.0.0.1:6379>?XREADGROUP?GROUP?group1?consumer?COUNT?5?STREAMS?queue?> 1)?1)?"queue"2)?1)?1)?"1618470740565-0"2)?1)?"name"2)?"zhangsan"2)?1)?"1618470743793-0"2)?1)?"name"2)?"lisi"同樣地,第二個消費組開始消費:
//?group2的consumer開始消費,>表示拉取最新數據 127.0.0.1:6379>?XREADGROUP?GROUP?group2?consumer?COUNT?5?STREAMS?queue?> 1)?1)?"queue"2)?1)?1)?"1618470740565-0"2)?1)?"name"2)?"zhangsan"2)?1)?"1618470743793-0"2)?1)?"name"2)?"lisi"我們可以看到,這 2 組消費者,都可以獲取同一批數據進行處理了。
這樣一來,就達到了多組消費者「訂閱」消費的目的。
3) 消息處理時異常,Stream 能否保證消息不丟失,重新消費?
除了上面拉取消息時用到了消息 ID,這里為了保證重新消費,也要用到這個消息 ID。
當一組消費者處理完消息后,需要執行 XACK 命令告知 Redis,這時 Redis 就會把這條消息標記為「處理完成」。
//?group1下的?1618472043089-0?消息已處理完成 127.0.0.1:6379>?XACK?queue?group1?1618472043089-0如果消費者異常宕機,肯定不會發送 XACK,那么 Redis 就會依舊保留這條消息。
待這組消費者重新上線后,Redis 就會把之前沒有處理成功的數據,重新發給這個消費者。這樣一來,即使消費者異常,也不會丟失數據了。
//?消費者重新上線,0-0表示重新拉取未ACK的消息 127.0.0.1:6379>?XREADGROUP?GROUP?group1?consumer1?COUNT?5?STREAMS?queue?0-0 //?之前沒消費成功的數據,依舊可以重新消費 1)?1)?"queue"2)?1)?1)?"1618472043089-0"2)?1)?"name"2)?"zhangsan"2)?1)?"1618472045158-0"2)?1)?"name"2)?"lisi"4) Stream 數據會寫入到 RDB 和 AOF 做持久化嗎?
Stream 是新增加的數據類型,它與其它數據類型一樣,每個寫操作,也都會寫入到 RDB 和 AOF 中。
我們只需要配置好持久化策略,這樣的話,就算 Redis 宕機重啟,Stream 中的數據也可以從 RDB 或 AOF 中恢復回來。
5) 消息堆積時,Stream 是怎么處理的?
其實,當消息隊列發生消息堆積時,一般只有 2 個解決方案:
生產者限流:避免消費者處理不及時,導致持續積壓
丟棄消息:中間件丟棄舊消息,只保留固定長度的新消息
而 Redis 在實現 Stream 時,采用了第 2 個方案。
在發布消息時,你可以指定隊列的最大長度,防止隊列積壓導致內存爆炸。
//?隊列長度最大10000 127.0.0.1:6379>?XADD?queue?MAXLEN?10000?*?name?zhangsan "1618473015018-0"當隊列長度超過上限后,舊消息會被刪除,只保留固定長度的新消息。
這么來看,Stream 在消息積壓時,如果指定了最大長度,還是有可能丟失消息的。
除了以上介紹到的命令,Stream 還支持查看消息長度(XLEN)、查看消費者狀態(XINFO)等命令,使用也比較簡單,你可以查詢官方文檔了解一下,這里就不過多介紹了。
好了,通過以上介紹,我們可以看到,Redis 的 Stream 幾乎覆蓋到了消息隊列的各種場景,是不是覺得很完美?
既然它的功能這么強大,這是不是意味著,Redis 真的可以作為專業的消息隊列中間件來使用呢?
但是還「差一點」,就算 Redis 能做到以上這些,也只是「趨近于」專業的消息隊列。
原因在于 Redis 本身的一些問題,如果把其定位成消息隊列,還是有些欠缺的。
到這里,就不得不把 Redis 與專業的隊列中間件做對比了。
下面我們就來看一下,Redis 在作隊列時,到底還有哪些欠缺?
與專業的消息隊列對比
其實,一個專業的消息隊列,必須要做到兩大塊:
消息不丟
消息可堆積
前面我們討論的重點,很大篇幅圍繞的是第一點展開的。
這里我們換個角度,從一個消息隊列的「使用模型」來分析一下,怎么做,才能保證數據不丟?
使用一個消息隊列,其實就分為三大塊:生產者、隊列中間件、消費者。
消息是否會發生丟失,其重點也就在于以下 3 個環節:
生產者會不會丟消息?
消費者會不會丟消息?
隊列中間件會不會丟消息?
1) 生產者會不會丟消息?
當生產者在發布消息時,可能發生以下異常情況:
消息沒發出去:網絡故障或其它問題導致發布失敗,中間件直接返回失敗
不確定是否發布成功:網絡問題導致發布超時,可能數據已發送成功,但讀取響應結果超時了
如果是情況 1,消息根本沒發出去,那么重新發一次就好了。
如果是情況 2,生產者沒辦法知道消息到底有沒有發成功?所以,為了避免消息丟失,它也只能繼續重試,直到發布成功為止。
生產者一般會設定一個最大重試次數,超過上限依舊失敗,需要記錄日志報警處理。
也就是說,生產者為了避免消息丟失,只能采用失敗重試的方式來處理。
但發現沒有?這也意味著消息可能會重復發送。
是的,在使用消息隊列時,要保證消息不丟,寧可重發,也不能丟棄。
那消費者這邊,就需要多做一些邏輯了。
對于敏感業務,當消費者收到重復數據數據時,要設計冪等邏輯,保證業務的正確性。
從這個角度來看,生產者會不會丟消息,取決于生產者對于異常情況的處理是否合理。
所以,無論是 Redis 還是專業的隊列中間件,生產者在這一點上都是可以保證消息不丟的。
2) 消費者會不會丟消息?
這種情況就是我們前面提到的,消費者拿到消息后,還沒處理完成,就異常宕機了,那消費者還能否重新消費失敗的消息?
要解決這個問題,消費者在處理完消息后,必須「告知」隊列中間件,隊列中間件才會把標記已處理,否則仍舊把這些數據發給消費者。
這種方案需要消費者和中間件互相配合,才能保證消費者這一側的消息不丟。
無論是 Redis 的 Stream,還是專業的隊列中間件,例如 RabbitMQ、Kafka,其實都是這么做的。
所以,從這個角度來看,Redis 也是合格的。
3) 隊列中間件會不會丟消息?
前面 2 個問題都比較好處理,只要客戶端和服務端配合好,就能保證生產端、消費端都不丟消息。
但是,如果隊列中間件本身就不可靠呢?
畢竟生產者和消費這都依賴它,如果它不可靠,那么生產者和消費者無論怎么做,都無法保證數據不丟。
在這個方面,Redis 其實沒有達到要求。
Redis 在以下 2 個場景下,都會導致數據丟失。
AOF 持久化配置為每秒寫盤,但這個寫盤過程是異步的,Redis 宕機時會存在數據丟失的可能
主從復制也是異步的,主從切換時,也存在丟失數據的可能(從庫還未同步完成主庫發來的數據,就被提成主庫)
基于以上原因我們可以看到,Redis 本身的無法保證嚴格的數據完整性。
所以,如果把 Redis 當做消息隊列,在這方面是有可能導致數據丟失的。
再來看那些專業的消息隊列中間件是如何解決這個問題的?
像 RabbitMQ 或 Kafka 這類專業的隊列中間件,在使用時,一般是部署一個集群,生產者在發布消息時,隊列中間件通常會寫「多個節點」,以此保證消息的完整性。這樣一來,即便其中一個節點掛了,也能保證集群的數據不丟失。
也正因為如此,RabbitMQ、Kafka在設計時也更復雜。畢竟,它們是專門針對隊列場景設計的。
但 Redis 的定位則不同,它的定位更多是當作緩存來用,它們兩者在這個方面肯定是存在差異的。
最后,我們來看消息積壓怎么辦?
4) 消息積壓怎么辦?
因為 Redis 的數據都存儲在內存中,這就意味著一旦發生消息積壓,則會導致 Redis 的內存持續增長,如果超過機器內存上限,就會面臨被 OOM 的風險。
所以,Redis 的 Stream 提供了可以指定隊列最大長度的功能,就是為了避免這種情況發生。
但 Kafka、RabbitMQ 這類消息隊列就不一樣了,它們的數據都會存儲在磁盤上,磁盤的成本要比內存小得多,當消息積壓時,無非就是多占用一些磁盤空間,相比于內存,在面對積壓時也會更加「坦然」。
綜上,我們可以看到,把 Redis 當作隊列來使用時,始終面臨的 2 個問題:
Redis 本身可能會丟數據
面對消息積壓,Redis 內存資源緊張
到這里,Redis 是否可以用作隊列,我想這個答案你應該會比較清晰了。
如果你的業務場景足夠簡單,對于數據丟失不敏感,而且消息積壓概率比較小的情況下,把 Redis 當作隊列是完全可以的。
而且,Redis 相比于 Kafka、RabbitMQ,部署和運維也更加輕量。
如果你的業務場景對于數據丟失非常敏感,而且寫入量非常大,消息積壓時會占用很多的機器資源,那么我建議你使用專業的消息隊列中間件。
總結
好了,總結一下。這篇文章我們從「Redis 能否用作隊列」這個角度出發,介紹了 List、Pub/Sub、Stream 在做隊列的使用方式,以及它們各自的優劣。
之后又把 Redis 和專業的消息隊列中間件做對比,發現 Redis 的不足之處。
最后,我們得出 Redis 做隊列的合適場景。
這里我也列了一個表格,總結了它們各自的優缺點。
????????
往期推薦
為什么大家都在抵制用定時任務實現「關閉超時訂單」功能?
Gartner 發布 2022 年汽車行業五大技術趨勢
別再用 Redis List 實現消息隊列了,Stream 專為隊列而生
OpenStack 如何跨版本升級
點分享
點收藏
點點贊
點在看
總結
以上是生活随笔為你收集整理的Redis 究竟适不适合当队列来用?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 新功能:在负载均衡SLB控制台上查看DD
- 下一篇: “刺激的”2017双11 阿里安全工程师