天天看點

redis和mysql實作消息隊列_基于Redis實作分布式消息隊列

1、Redis是什麼鬼?

Redis是一個簡單的,高效的,分布式的,基于記憶體的緩存工具。

假設好伺服器後,通過網絡連接配接(類似資料庫),提供Key-Value式緩存服務。

簡單,是Redis突出的特色。

簡單可以保證核心功能的穩定和優異。

2、性能

性能方面:Redis是足夠高效的。

和Memecached對比,在資料量較小大情況下,Redis性能更優秀。

資料量大到一定程度的時候,Memecached性能稍好。

簡單結論:但總體上講Redis性能已經足夠好。

經實驗得知:

List操作和字元串操作性能相當,略差,幾乎可以忽略。

使用Jedis自帶pool,“每次從pool中取用完放回“ 和 “重用單個連接配接“

相比,平均用時是3倍。這部分需要繼續研究底層機制,采用更合理的實驗方法進一步獲得資料。

使用Jedis自帶pool,性能上是滿足目前通路量需要的,等有時間了再進一步深入。

3、資料類型

Redis支援5種資料類型:字元串、Map、List、Set、Sorted

Set。

List特别适合用于實作隊列。提供的操作包括:

從左側(或右側)放入一個元素,從右側(或左側)取出一個元素,讀取某個範圍的元素,删除某個範圍的元素。

Sorted

Set中元素是唯一的,可以通過名字找。

Map可以高效地通過key找。

假如我們需要實作finishTash(taskId),需要通過名字在隊列中找元素,上面兩個可能會用到。

4、原子操作

實作分布式隊列首要問題是:不能出現并發問題。

Redis是底層是單線程的,指令執行是原子操作,支援事務,契合了我們的需求。

Redis直接提供的指令都是原子操作,包括lpush、rpop、blpush、brpop等。

Redis支援事務。通過類似

begin…[cancel]…commit的文法,提供begin…commit之間的指令為原子操作的功能,之間指令對資料的改變對其他操作是不可見的。類似關系型資料庫中的存儲過程,同時提供了最進階别的事務隔離級别。

Redis支援腳本,每個腳本的執行是原子性的。

做了一下并發測試:

寫了個小程式,随機對List做push或pop操作,push的比pop的稍多。

記錄每次處理的詳細資訊到資料庫。

最後把List中資料都pop出來,詳細記錄每次pop詳細資訊。

統計push和pop是否相等,統計針對每條資料是否都有push和pop。

500并發,沒有出現并發問題。

5、叢集

實作分布式隊列另一個重要問題是:不能出現單點故障。

Redis支援Master-Slave資料複制,從伺服器設定 slave-of master-ip:port

即可。

叢集功能可以由用戶端提供。

用戶端使用哨兵,可自動切換主伺服器。

由于隊列操作都是寫操作,從伺服器主要目的是備份資料,保證資料安全。

如果想基于

sharding 做多master叢集,可以結合 zookeeper 自己做。

Redis

3.0支援叢集了,還沒細看,應該是個好消息,等大家都用起來,沒什麼問題的話,可以考慮試試看。

如果 master

宕掉,怎麼辦?

“哨兵”會選出一個新的master來。産生過程中,消息隊列暫停服務。

最極端的情況,所有Redis都停了,當消息隊列發現Redis停止響應時,對業務系統的請求應抛出異常,停止隊列服務。

這樣會影響業務,業務系統下訂單、審批等操作會失敗。如果可以接受,這是一種方案。

Redis整個叢集宕掉,這種情況很少發生,如果真發生了,業務系統停止服務也是可以了解的。

如果想要在Redis整個叢集宕掉的情況下,消息隊列仍繼續提供服務。

方法是這樣的:

啟用備用存儲機制,可以是zookeeper、可以是關系型資料庫、可以是另外可用的Memecached等。

本地記憶體存儲是不可取的,首先,同步多個用戶端虛拟機記憶體資料太複雜,相當于自己實作了一個Redis,其次,保證記憶體資料存儲安全太複雜。

備用存儲機制相當于實作了另外一個版本的消息隊列,邏輯一緻,底層存儲不同。這個實作可以性能低一些,保證最基本的原則即可。

想要保證不出現并發問題,由于消息隊列程式同時運作在多個虛拟機中,對象鎖、方法鎖無效。需要有一個獨立于虛拟機的鎖機制,zookeeper是個好選擇。

将關系型資料庫設定為最進階别的事務隔離級别,太傻了。除了zk有其他好辦法嗎?

Redis叢集整個宕掉的同時Zookeeper也全軍覆沒怎麼辦?

這個問題是沒有盡頭的,提供了第二備用存儲、第三備用存儲、第四備用存儲、…,理論上也會同時宕掉,那時候怎麼辦?

有錢任性的土豪可以繼續,預算有限的情況,能做到哪步就做到哪步。

6、持久化

分布式隊列的應用場景和緩存的應用場景是不一樣的。

如果有沒來得及持久化的資料怎麼辦?

從業務系統的角度,已經成功發送給消息隊列了。

消息隊列也以為Redis妥妥地收好了。

可Redis還沒寫到日記裡,更沒有及時通知小夥伴,挂了。可能是斷電了,可能是程序被kill了。

後果會怎樣?

已經執行過的任務會再次執行一遍。

已經放到隊列中的任務,消失了。

标記為已經完成的任務,狀态變為“進行中”了,然後又被執行了一遍。

後果不可接受。

分布式隊列不允許丢資料。

從業務角度,哪怕丢1條資料也是無法接受的。

從運維角度,Redis丢資料後,如果可以及時發現并補救,也是可以接受的。

從架構角度,隊列儲存在Redis中,業務資料(包括任務狀态)儲存在關系型資料庫中。

任務狀态是從業務角度确定的,消息隊列不應該幹涉。如果業務狀态沒有統一的規範和定義,從業務資料比對任務隊列是否全面正确,就隻能交給業務開發方來做。

從分工上來看,任務隊列的目的是管理任務執行的狀态,業務系統把這個職責交給了任務隊列,業務系統自身的任務狀态維護未必準确。

結論:任務隊列不能推卸責任,不能丢資料是核心功能,不能打折扣。

采用

Master-Slave 資料複制模式,配置bgsave,追加存儲到aof。

在從伺服器上配置bgsave,不影響master性能。

隊列操作都是寫操作,master任務繁重,能讓slave分擔的持久化工作,就不要master做。

rdb和aof兩種方法都用上,多重保險。

appendfsync設為always。// 單節點測性能,連續100000次算平均時間,和per

second比對,性能損失不大。

性能會有些許損失,但任務執行為異步操作,無需使用者同步等待,為了保證資料安全,這樣是值得的。

當運維需要重新開機Master伺服器的時候,采取這樣的順序:

1. 通過 cli shutdown 停止master伺服器,

master交代完後事後,關掉自己。這時候“哨兵”會找一個新的master出來。

萬萬不可以直接kill或者直接打開防火牆中斷master和slave之間的連接配接。

master 對外防火牆,停止對外服務,Master 自動切換到其他伺服器上, 原 Master 繼續持久化

aof,發送到原來各從伺服器。

2. 在原 master 上進行運維操作。

3. 啟動原 master,這時候它已經是從伺服器了。耐心等待它從新 master 擷取最新資料。觀察 redis

日志輸出,确認資料安全。

4. 對新的 master 重複1-3的操作。

5. 将以上操作寫成腳本,自動化執行,避免人為錯誤。