Redis Cluster的FailOver失敗案例分析

jopen 9年前發布 | 25K 次閱讀 Redis NoSQL數據庫

場景:

      使用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設計(http://vdisk.weibo.com/s/u78RGlrhC7FF/1422958608)。

問題定位:

      看完上面的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(https://github.com/antirez/redis/issues/2285)給作者 。作者看了以后,不確定副作用,但他覺得需要一個更加通用的解決方案。其實調大這個參數只是治標不治本,問題的根源是Gossip太慢了,隨機挑選3個節點,并且選中PFail的節點還需要有一定的概率。是不是可以優先考慮從PFail的節點集合中隨機選出一部分,再從正常節點中選出一部分,這就兼顧了 PFail和新加入的節點,就又提了個issue(https://github.com/antirez/redis/issues/2336)。作者先做了一個調整,Gossip攜帶節點的數量不再是3而是集群規模的1/10,他認為1/10是一個魔數,具體原因在cluster.c的 clusterSendPing函數中有描述。然后在這個版本上進行測試,修改cluster-node-timeout為5s,發現Master的失敗判定只需要12s,這個時間包括PFail的標記和PFai->Fail的轉換。然而slave的失敗判定就不是很穩定了在20~70s之間。我覺得還不是很理想,作者說他測試的結果比我的要理想很多,并且說他又做了一些優化,還是先發布RC3吧。我比對了RC3和我測試版本的區別,發現作者在 Gossip的時候添加對PFail或Fail節點的偏好,我重新在RC3上測試,結果很理想,Master失敗判定需要9s,Slave需要11s并且很穩定。


結論:

      如果在較大規模的RedisCluster集群上遇到這個問題,果然地升級RC3吧。

來自:http://my.oschina.net/zhanghailei/blog/375561

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!