微信搜索關注「水滴與銀彈」公衆號,第一時間獲取優質技術乾貨。7年資深後端研發,給你呈現不同的技術視角。python
你們好,我是 Kaito。程序員
我常常聽到不少人討論,關於「把 Redis 看成隊列來用是否合適」的問題。redis
有些人表示同意,他們認爲 Redis 很輕量,用做隊列很方便。shell
也些人則反對,認爲 Redis 會「丟」數據,最好仍是用「專業」的隊列中間件更穩妥。後端
究竟哪一種方案更好呢?緩存
這篇文章,我就和你聊一聊把 Redis 看成隊列,到底是否合適這個問題。微信
我會從簡單到複雜,一步步帶你梳理其中的細節,把這個問題真正的講清楚。markdown
看完這篇文章後,我但願你對這個問題你會有全新的認識。網絡
在文章的最後,我還會告訴你關於「技術選型」的思路,文章有點長,但願你能夠耐心讀完。運維
首先,咱們先從最簡單的場景開始講起。
若是你的業務需求足夠簡單,想把 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")
// 沒有消息,休眠2s
if 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 中 POP 一條消息出來後,這條消息就會當即從鏈表中刪除了。也就是說,不管消費者是否處理成功,這條消息都沒辦法再次消費了。
這也意味着,若是消費者在處理消息時異常宕機,那這條消息就至關於丟失了。
針對這 2 個問題怎麼解決呢?咱們一個個來看。
從名字就能看出來,這個模塊是 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 最大問題是:丟數據。
若是發生如下場景,就有可能致使數據丟失:
到底是怎麼回事?
這其實與 Pub/Sub 的實現方式有很大關係。
Pub/Sub 在實現時很是簡單,它沒有基於任何數據類型,也沒有作任何的數據存儲,它只是單純地爲生產者、消費者創建「數據轉發通道」,把符合規則的數據,從一端轉發到另外一端。
一個完整的發佈、訂閱消息處理流程是這樣的:
看到了麼,整個過程當中,沒有任何的數據存儲,一切都是實時轉發的。
這種設計方案,就致使了上面提到的那些問題。
例如,若是一個消費者異常掛掉了,它再從新上線後,只能接收新的消息,在下線期間生產者發佈的消息,由於找不到消費者,都會被丟棄掉。
若是全部消費者都下線了,那生產者發佈的消息,由於找不到任何一個消費者,也會所有「丟棄」。
因此,當你在使用 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。
它的參數含義以下:
Pub/Sub 的這一點特色,是與 List 做隊列差別比較大的。
從這裏你應該能夠看出,List 實際上是屬於「拉」模型,而 Pub/Sub 其實屬於「推」模型。
List 中的數據能夠一直積壓在內存中,消費者何時來「拉」均可以。
但 Pub/Sub 是把消息先「推」到消費者在 Redis Server 上的緩衝區中,而後等消費者再來取。
當生產、消費速度不匹配時,就會致使緩衝區的內存開始膨脹,Redis 爲了控制緩衝區的上限,因此就有了上面講到的,強制把消費者踢下線的機制。
好了,如今咱們總結一下 Pub/Sub 的優缺點:
有沒有發現,除了第一個是優勢以外,剩下的都是缺點。
因此,不少人看到 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 經過 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 經過如下命令完成發佈訂閱:
下面咱們來看具體如何作?
首先,生產者依舊發佈 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 個場景下,都會致使數據丟失。
基於以上緣由咱們能夠看到,Redis 自己的沒法保證嚴格的數據完整性。
因此,若是把 Redis 當作消息隊列,在這方面是有可能致使數據丟失的。
再來看那些專業的消息隊列中間件是如何解決這個問題的?
像 RabbitMQ 或 Kafka 這類專業的隊列中間件,在使用時,通常是部署一個集羣,生產者在發佈消息時,隊列中間件一般會寫「多個節點」,以此保證消息的完整性。這樣一來,即使其中一個節點掛了,也能保證集羣的數據不丟失。
也正由於如此,RabbitMQ、Kafka在設計時也更復雜。畢竟,它們是專門針對隊列場景設計的。
但 Redis 的定位則不一樣,它的定位更可能是看成緩存來用,它們二者在這個方面確定是存在差別的。
最後,咱們來看消息積壓怎麼辦?
4) 消息積壓怎麼辦?
由於 Redis 的數據都存儲在內存中,這就意味着一旦發生消息積壓,則會致使 Redis 的內存持續增加,若是超過機器內存上限,就會面臨被 OOM 的風險。
因此,Redis 的 Stream 提供了能夠指定隊列最大長度的功能,就是爲了不這種狀況發生。
但 Kafka、RabbitMQ 這類消息隊列就不同了,它們的數據都會存儲在磁盤上,磁盤的成本要比內存小得多,當消息積壓時,無非就是多佔用一些磁盤空間,相比於內存,在面對積壓時也會更加「坦然」。
綜上,咱們能夠看到,把 Redis 看成隊列來使用時,始終面臨的 2 個問題:
到這裏,Redis 是否能夠用做隊列,我想這個答案你應該會比較清晰了。
若是你的業務場景足夠簡單,對於數據丟失不敏感,並且消息積壓機率比較小的狀況下,把 Redis 看成隊列是徹底能夠的。
並且,Redis 相比於 Kafka、RabbitMQ,部署和運維也更加輕量。
若是你的業務場景對於數據丟失很是敏感,並且寫入量很是大,消息積壓時會佔用不少的機器資源,那麼我建議你使用專業的消息隊列中間件。
好了,總結一下。這篇文章咱們從「Redis 可否用做隊列」這個角度出發,介紹了 List、Pub/Sub、Stream 在作隊列的使用方式,以及它們各自的優劣。
以後又把 Redis 和專業的消息隊列中間件作對比,發現 Redis 的不足之處。
最後,咱們得出 Redis 作隊列的合適場景。
這裏我也列了一個表格,總結了它們各自的優缺點。
最後,我想和你再聊一聊關於「技術方案選型」的問題。
你應該也看到了,這篇文章雖然始於 Redis,但並不止於 Redis。
咱們在分析 Redis 細節時,一直在提出問題,而後尋找更好的解決方案,在文章最後,又聊到一個專業的消息隊列應該怎麼作。
其實,咱們在討論技術選型時,就是一個關於如何取捨的問題。
而這裏我想傳達給你的信息是,在面對技術選型時,不要不通過思考就以爲哪一個方案好,哪一個方案很差。
你須要根據具體場景具體分析,這裏我把這個分析過程分爲 2 個層面:
這篇文章所講到的內容,都是以業務功能角度出發作決策的。
但這裏的第二點,從技術資源角度出發,其實也很重要。
技術資源的角度是說,你所處的公司環境、技術資源可否匹配這些技術方案。
這個怎麼解釋呢?
簡單來說,就是你所在的公司、團隊,是否有匹配的資源能 hold 住這些技術方案。
咱們都知道 Kafka、RabbitMQ 是很是專業的消息中間件,但它們的部署和運維,相比於 Redis 來講,也會更復雜一些。
若是你在一個大公司,公司自己就有優秀的運維團隊,那麼使用這些中間件確定沒問題,由於有足夠優秀的人能 hold 住這些中間件,公司也會投入人力和時間在這個方向上。
但若是你是在一個初創公司,業務正處在快速發展期,暫時沒有能 hold 住這些中間件的團隊和人,若是貿然使用這些組件,當發生故障時,排查問題也會變得很困難,甚至會阻礙業務的發展。
而這種情形下,若是公司的技術人員對於 Redis 都很熟,綜合評估來看,Redis 也基本能夠知足業務 90% 的需求,那當下選擇 Redis 未必不是一個好的決策。
因此,作技術選型不僅是技術問題,還與人、團隊、管理、組織結構有關。
也正是由於這些緣由,當你在和別人討論技術選型問題時,你會發現每一個公司的作法都不相同。
畢竟每一個公司所處的環境和文化不同,作出的決策固然就會各有差別。
若是你不瞭解這其中的邏輯,那在作技術選型時,只會趨於表面現象,沒法深刻到問題根源。
而一旦你理解了這個邏輯,那麼你在看待這個問題時,不只對於技術會有更加深入認識,對技術資源和人的把握,也會更加清晰。
但願你之後在作技術選型時,可以把這些因素也考慮在內,這對你的技術成長之路也是很是有幫助的。
想看更多硬核技術文章?歡迎關注個人公衆號「水滴與銀彈」。
我是 Kaito,是一個對於技術有思考的資深後端程序員,在個人文章中,我不只會告訴你一個技術點是什麼,還會告訴你爲何這麼作?我還會嘗試把這些思考過程,提煉成通用的方法論,讓你能夠應用在其它領域中,作到觸類旁通。