Redis Cluster的FailOver失敗案例分析
場景:
使用redis clusterRC1部署叢集,6臺機器,每臺部署16個例項,每個master使用一個slave,node_timeout為預設值(15s)。kill掉其中一個master發現failover完成不了。通過cluster nodes觀察,該節點一直處於pfail狀態。問題出在失敗判定上,一直處於PFail,說明完成不了PFail->Fail的轉換。然而同樣的配置在32節點的叢集中,Failover一點問題沒有。
FailOver設計實現:
Failover,通俗地說,一個master有N(N>=1)個slave,當master掛掉以後,能選出一個slave晉升成Master繼續提供服務。Failover由失敗判定和Leader選舉兩部分組成,Redis Cluster採用去中心化(Gossip)的設計,每個節點通過傳送Ping(包括Gossip資訊)/Pong心跳的方式來探測對方節點的存活,如果心跳超時則標記對方節點的狀態為PFail,這個意思是說該節點認為對方節點可能失敗了,有可能是網路閃斷或者分割槽等其他原因導致通訊失敗。例如節點A給節點B發Ping/Pong心跳超時,則A將B標記為PFAIL,強調一點,僅是在A看來B節點失敗。要完全判定B失敗,則需要一種協商的方式,需要叢集中一半以上的Master節點認為B處於PFail狀態,才會正式將節點B標記為Fail。那麼問題來了,Master之間如何交換意見呢,或者說節點A如何知道其他Master也將B標記為PFfail了,並且能統計出是否有一半以上的Master認為B為PFail呢?前面提到Gossip,Gossip的主要作用就是資訊交換,在A給C發Ping的時候,A將已知節點隨機挑選三個節點新增到Ping包中發給C。既然是隨機,經過多次Gossip以後,A會將處於PFail的B告訴給C。在節點C上,B節點有一個失敗報告的連結串列,A告訴C,B可能失敗,將A節點新增到B節點的失敗報告連結串列中。經過叢集中所有節點之間多次Gossip,一旦B的失敗報告數量超過Master數量的一半以上,就立即標記B為Fail並廣播給整個叢集。那這樣還會有一個問題,假設一天之內失敗報告的數量超過Master的一半以上,同時報告的時間間隔又比較大,那麼就會產生誤判。所以得給失敗報告加上一個有效期,在一定的時間視窗內,失敗報告的數量超過Master的一半以上以後標記為Fail,這樣才能避免誤判。至此就把失敗判定說完了,剩下還有Leader選舉,Redis
Cluster採用類似Raft的演算法,有一點不同的是並不是slave之間進行投票,而是在所有Master中間進行投票。這樣做的好處就是即使一主一從也能完成選舉,Redis Cluster這樣做也是有道理。slave不提供任務服務,如果允許掛N個節點,就得部署(2N+1)個slave,這是資源的極大浪費。Leader選舉和主題不太相關就不細講了,我寫了一個PPT講Redis Cluster的Failover設計(
問題定位:
看完上面的FailOver設計實現,問題就不難定位了。在時間視窗內,失敗報告的數量沒有達到Master的一半以上,所以完成不了Pfail到Fail的轉換。Redis Cluster的這個時間視窗是cluster-node-timeout * REDIS_CLUSTER_FAIL_REPORT_VALIDITY_MULT,其中REDIS_CLUSTER_FAIL_REPORT_VALIDITY_MULT=2,是一個常量,不能修改。那麼預設的失敗報告有效期是30s,在30s內不能收集到Master的一半以上的失敗報告,新加入的失敗報告趕不上失效的速度,所以一直完不成PFail到Fail的轉換,這就是問題所在。我首先想到調大REDIS_CLUSTER_FAIL_REPORT_VALIDITY_MULT到10,Failover能成功了,但耗時過長至少要在60s以上,並且不知要調大了有沒有副作用,就提了一個issue(
結論:
如果在較大規模的RedisCluster叢集上遇到這個問題,果然地升級RC3吧。