技術格言
世界上並沒有完美的程序,但是我們並不因此而沮喪,因為寫程序就是一個不斷追求完美的過程。
什么是腦裂
字面含義
首先,腦裂從字面上理解就是腦袋裂開了,就是思想分家了,就是有了兩個山頭,就是有了兩個主思想。
技術定義
在高可用集群中,當兩台高可用服務器在指定的時間內,由於網絡的原因無法互相檢測到對方心跳而各自啟動故障轉移功能,取得了資源以及服務的所有權,而此時的兩台高可用服務器對都還活着並作正常運行,這樣就會導致同一個服務在兩端同時啟動而發生沖突的嚴重問題,最嚴重的就是兩台主機同時占用一個VIP的地址(類似雙端導入概念),當用戶寫入數據的時候可能會分別寫入到兩端,這樣可能會導致服務器兩端的數據不一致或造成數據的丟失,這種情況就稱為裂腦,也有的人稱之為分區集群或者大腦垂直分隔,互相接管對方的資源,出現多個Master的情況,稱為腦裂。

腦裂導致的問題
引起數據的不完整性:在集群節點出現腦裂的時候,如果外部無法判斷哪個為主節點,腦裂的集群都可以正常訪問的時候,這時候就會出現數據不完整的可能性。
- 服務異常:對外提供服務出現異常。
導致裂腦發生的原因
優先考慮心跳線路上的問題,在可能是心跳服務,軟件層面的問題
-
1)高可用服務器對之間心跳線路故障,導致無法正常的通信。原因比如:
-
1——心跳線本身就壞了(包括斷了,老化);
-
2——網卡以及相關驅動壞了,IP配置及沖突問題;
-
3——心跳線間連接的設備故障(交換機的故障或者是網卡的故障);
-
4——仲裁的服務器出現問題。
-
-
2)高可用服務器對上開啟了防火牆阻擋了心跳消息的傳輸;
-
3)高可用服務器對上的心跳網卡地址等信息配置的不正確,導致發送心跳失敗;
-
4)其他服務配置不當等原因,如心跳的方式不同,心跳廣播沖突,軟件出現了BUG等。
解決腦裂所出現的問題
-
添加冗余的心跳線,盡量減少“腦裂”的機會
-
啟用磁盤鎖:在發生腦裂的時候可以協調控制對資源的訪問設置仲裁機制
實際的生產環境中,我們可以從以下幾個方面來防止裂腦的發生:
-
1)同時使用串行電纜和以太網電纜連接,同時用兩條心跳線路,這樣一條線路壞了,另一個線路還是好的,依然能傳送消息(推薦的)
-
2)檢測到裂腦的時候強行的關閉一個心跳節點(需要特殊的節點支持,如stonith,fence),相當於程序上備節點發現心跳線故障,發送關機命令到主節點。
-
3)多節點集群中,可以通過增加仲裁的機制,確定誰該獲得資源,這里面有幾個參考的思路:
1——增加一個仲裁機制。例如設置參考的IP,當心跳完全斷開的時候,2個節點各自都ping一下參考的IP,不同則表明斷點就出現在本段,這樣就主動放棄競爭,讓能夠ping通參考IP的一端去接管服務。
2——通過第三方軟件仲裁誰該獲得資源,這個在阿里有類似的軟件應用
-
4)做好對裂腦的監控報警(如郵件以及手機短信等),在問題發生的時候能夠人為的介入到仲裁,降低損失。當然,在實施高可用方案的時候,要根據業務的實際需求確定是否能夠容忍這樣的損失。對於一般的網站業務,這個損失是可控的(公司使用)
-
5)啟用磁盤鎖。正在服務一方鎖住共享磁盤,腦裂發生的時候,讓對方完全搶不走共享的磁盤資源。但使用鎖磁盤也會有一個不小的問題,如果占用共享盤的乙方不主動解鎖,另一方就永遠得不到共享磁盤。現實中介入服務節點突然死機或者崩潰,另一方就永遠不可能執行解鎖命令。后備節點也就截關不了共享的資源和應用服務。於是有人在HA中涉及了“智能”鎖,正在服務的一方只在發現心跳線全部斷開時才啟用磁盤鎖,平時就不上鎖了
什么是redis腦裂?
如果在redis中,形式上就是有了兩個master,記住兩個master才是腦裂的前提。
哨兵(sentinel)模式下的腦裂

1個master與3個slave組成的哨兵模式(哨兵獨立部署於其它機器),剛開始時,2個應用服務器server1、server2都連接在master上,如果master與slave及哨兵之間的網絡發生故障,但是哨兵與slave之間通訊正常,這時3個slave其中1個經過哨兵投票后,提升為新master,如果恰好此時server1仍然連接的是舊的master,而server2連接到了新的master上。
數據就不一致了,基於setNX指令的分布式鎖,可能會拿到相同的鎖;基於incr生成的全局唯一id,也可能出現重復。
集群(cluster)模式下的腦裂

cluster模式下,這種情況要更復雜,例如集群中有6組分片,每給分片節點都有1主1從,如果出現網絡分區時,各種節點之間的分區組合都有可能。
手動解決問題
在正常情況下,如果master掛了,那么寫入就會失敗,如果是手動解決,那么人為會檢測master以及slave的網絡狀況,然后視情況,如果是master掛了,重啟master,如果是master與slave之間的連接斷了,可以調試網絡,這樣雖然麻煩,但是是可以保證只有一個master的,所以只要認真負責,不會出現腦裂。
自動解決問題
Redis中有一個哨兵機制,哨兵機制的作用就是通過redis哨兵來檢測redis服務的狀態,如果一旦發現master掛了,就在slave中選舉新的master節點以實現故障自動轉移。
問題,就出現在這個自動故障轉移上,如果是哨兵和slave同時與master斷了聯系,即哨兵可以監測到slave,但是監測不到master,而master雖然連接不上slave和哨兵,但是還是在正常運行,這樣如果哨兵因為監測不到master,認為它掛了,會在slave中選舉新的master,而有一部分應用仍然與舊的master交互。當舊的master與新的master重新建立連接,舊的master會同步新的master中的數據,而舊的master中的數據就會丟失。所以我認為redis腦裂就是自動故障轉移造成的。
總結梳理解決方案
如何解決腦裂?
設置每個master限制slave的數量
redis的配置文件中,存在兩個參數
min-slaves-to-write 3
min-slaves-max-lag 10
- 第一個參數表示連接到master的最少slave數量
- 第二個參數表示slave連接到master的最大延遲時間
按照上面的配置,要求至少3個slave節點,且數據復制和同步的延遲不能超過10秒,否則的話master就會拒絕寫請求,配置了這兩個參數之后,如果發生集群腦裂,原先的master節點接收到客戶端的寫入請求會拒絕,就可以減少數據同步之后的數據丟失。
注意:較新版本的redis.conf文件中的參數變成了
min-replicas-to-write 3
min-replicas-max-lag 10
redis中的異步復制情況下的數據丟失問題也能使用這兩個參數
總結
官方文檔所言,redis並不能保證強一致性(Redis Cluster is not able to guarantee strong consistency. / In general Redis + Sentinel as a whole are a an eventually consistent system) 對於要求強一致性的應用,更應該傾向於相信RDBMS(傳統關系型數據庫)。
