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. 将以上操作寫成腳本,自動化執行,避免人為錯誤。