Redis主從集群切換數據丟失的解決方案

一、數據丟失的情況

異步復制同步丟失

集群產生腦裂數據丟失

1.異步復制丟失

對於Redis主節點與從節點之間的數據復制,是異步復制的,當客戶端發送寫請求給master節點的時候,客戶端會返回OK,然後同步到各個slave節點中。

如果此時master還沒來得及同步給slave節點時發生宕機,那麼master內存中的數據會丟失;

要是master中開啟持久化設置數據可不可以保證不丟失呢?答案是否定的。在master 發生宕機後,sentinel集群檢測到master發生故障,重新選舉新的master,如果舊的master在故障恢復後重啟,那麼此時它需要同步新master的數據,此時新的master的數據是空的(假設這段時間中沒有數據寫入)。那麼舊master中的數據就會被刷新掉,此時數據還是會丟失。

2.集群產生腦裂

首先我們需要理解集群的腦裂現象,這就好比一個人有兩個大腦,那麼到底受誰來控制呢?在分佈式集群中,分佈式協作框架zookeeper很好的解決瞭這個問題,通過控制半數以上的機器來解決。

那麼在Redis中,集群腦裂產生數據丟失的現象是怎麼樣的呢?

假設我們有一個redis集群,正常情況下client會向master發送請求,然後同步到salve,sentinel集群監控著集群,在集群發生故障時進行自動故障轉移。

此時,由於某種原因,比如網絡原因,集群出現瞭分區,master與slave節點之間斷開瞭聯系,sentinel監控到一段時間沒有聯系認為master故障,然後重新選舉,將slave切換為新的master。但是master可能並沒有發生故障,隻是網絡產生分區,此時client任然在舊的master上寫數據,而新的master中沒有數據,如果不及時發現問題進行處理可能舊的master中堆積大量數據。在發現問題之後,舊的master降為slave同步新的master數據,那麼之前的數據被刷新掉,大量數據丟失。

在瞭解瞭上面的兩種數據丟失場景後,我們如何保證數據可以不丟失呢?在分佈式系統中,衡量一個系統的可用性,我們一般情況下會說4個9,5個9的系統達到瞭高可用(99.99%,99.999%,據說淘寶是5個9)。對於redis集群,我們不可能保證數據完全不丟失,隻能做到使得盡量少的數據丟失。

二、如何保證盡量少的數據丟失?

在redis的配置文件中有兩個參數我們可以設置:

min-slaves-to-write 1
min-slaves-max-lag 10

min-slaves-to-write默認情況下是0,min-slaves-max-lag默認情況下是10。

以上面配置為例,這兩個參數表示至少有1個salve的與master的同步復制延遲不能超過10s,一旦所有的slave復制和同步的延遲達到瞭10s,那麼此時master就不會接受任何請求。

我們可以減小min-slaves-max-lag參數的值,這樣就可以避免在發生故障時大量的數據丟失,一旦發現延遲超過瞭該值就不會往master中寫入數據。

那麼對於client,我們可以采取降級措施,將數據暫時寫入本地緩存和磁盤中,在一段時間後重新寫入master來保證數據不丟失;也可以將數據寫入kafka消息隊列,隔一段時間去消費kafka中的數據。

通過上面兩個參數的設置我們盡可能的減少數據的丟失,具體的值還需要在特定的環境下進行測試設置。

補充:Redis Cluster 會丟數據嗎?

Redis Cluster 不保證強一致性,在一些特殊場景,客戶端即使收到瞭寫入確認,還是可能丟數據的。

場景1:異步復制

client 寫入 master B

master B 回復 OK

master B 同步至 slave B1 B2 B3

B 沒有等待 B1 B2 B3 的確認就回復瞭 client,如果在 slave 同步完成之前,master 宕機瞭,其中一個 slave 會被選為 master,這時之前 client 寫入的數據就丟瞭。

wait 命令可以增強這種場景的數據安全性。

wait 會阻塞當前 client 直到之前的寫操作被指定數量的 slave 同步成功。

wait 可以提高數據的安全性,但並不保證強一致性。

因為即使使用瞭這種同步復制方式,也存在特殊情況:一個沒有完成同步的 slave 被選舉為瞭 master。

場景2:網絡分區

6個節點 A, B, C, A1, B1, C1,3個master,3個slave,還有一個client,Z1。

發生網絡分區之後,形成瞭2個區,A, C, A1, B1, C1 和 B Z1。

這時 Z1 還是可以向 B 寫入的,如果短時間內分區就恢復瞭,那就沒問題,整個集群繼續正常工作,但如果時間一長,B1 就會成為所在分區的 master,Z1 寫入 B 的數據就丟瞭。

maximum window(最大時間窗口) 可以減少數據損失,可以控制 Z1 向 B 寫入的總數:

過去一定時間後,分區的多數邊就會進行選舉,slave 成為 master,這時分區少數邊的 master 就會拒絕接收寫請求。

這個時間量是非常重要的,稱為節點過期時間。

一個 master 在達到過期時間後,就被認為是故障的,進入 error 狀態,停止接收寫請求,可以被 slave 取代。

小結

Redis Cluster 不保證強一致性,存在丟失數據的場景:

異步復制

在 master 寫成功,但 slave 同步完成之前,master 宕機瞭,slave 變為 master,數據丟失。

wait 命令可以給為同步復制,但也無法完全保證數據不丟,而且影響性能。

網絡分區

分區後一個 master 繼續接收寫請求,分區恢復後這個 master 可能會變為 slave,那麼之前寫入的數據就丟瞭。

可以設置節點過期時間,減少 master 在分區期間接收的寫入數量,降低數據丟失的損失。

以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。如有錯誤或未考慮完全的地方,望不吝賜教。