Redis腦裂現(xiàn)象
什么是Redis的腦裂現(xiàn)象
當(dāng)Redis主從集群環(huán)境出現(xiàn)兩個(gè)主節(jié)點(diǎn)為客戶端提供服務(wù),這時(shí)客戶端請(qǐng)求命令可能會(huì)發(fā)生數(shù)據(jù)丟失的情況。
腦裂出現(xiàn)的場(chǎng)景
場(chǎng)景一
主從哨兵集群中如果當(dāng)發(fā)生主從集群切換時(shí),那么一定是超過預(yù)設(shè)quorum數(shù)量的哨兵和主庫(kù)連接超時(shí)了,這時(shí)哨兵集群才會(huì)將主庫(kù)判斷為主觀下線,然后哨兵開始選舉新的主節(jié)點(diǎn),進(jìn)行故障轉(zhuǎn)移,轉(zhuǎn)移完畢后客戶端和新的主節(jié)點(diǎn)通信恢復(fù)正常請(qǐng)求。
如果在哨兵進(jìn)行選舉,故障轉(zhuǎn)移的過程中原主節(jié)點(diǎn)恢復(fù)和客戶端的通信,那么證明原主節(jié)點(diǎn)沒有真正的故障,這時(shí)客戶端依舊可以向原主節(jié)點(diǎn)正常通信,這就是腦裂產(chǎn)生的第一個(gè)場(chǎng)景,示意圖如下
假故障:
1、同服務(wù)器其它進(jìn)程占用大量CPU資源,導(dǎo)致主節(jié)點(diǎn)短時(shí)間無法響應(yīng)心跳,CPU資源空閑后恢復(fù)正常。
2、主庫(kù)自身阻塞,如處理bigkey或者發(fā)生內(nèi)存swap時(shí),短時(shí)間無法響應(yīng)心跳,阻塞解決后心跳恢復(fù)正常。
真故障:
1、服務(wù)器宕機(jī)。
2、實(shí)例進(jìn)程掛了。
場(chǎng)景二
網(wǎng)絡(luò)分區(qū),主節(jié)點(diǎn)和客戶端,哨兵和從庫(kù)分割為了兩個(gè)網(wǎng)絡(luò),主庫(kù)和客戶端處在一個(gè)網(wǎng)絡(luò)中,從庫(kù)和哨兵在另外一個(gè)網(wǎng)絡(luò)中,此時(shí)哨兵也會(huì)發(fā)起主從切換,出現(xiàn)兩個(gè)主節(jié)點(diǎn)的情況。
腦裂帶來的影響
腦裂出現(xiàn)后帶來最嚴(yán)重的后果就是數(shù)據(jù)丟失,為什么會(huì)出現(xiàn)數(shù)據(jù)丟失的問題呢,主要原因是新主庫(kù)確定后會(huì)向所有的實(shí)例發(fā)送slave of命令,讓所有實(shí)例重新進(jìn)行全量同步,而全量同步首先就會(huì)將實(shí)例上的數(shù)據(jù)先清空,所以在主從同步期間在原主庫(kù)執(zhí)行的命令將會(huì)被清空(上面場(chǎng)景二是同樣的道理,在網(wǎng)絡(luò)分區(qū)恢復(fù)后原主節(jié)點(diǎn)將被降級(jí)為從節(jié)點(diǎn),并且執(zhí)行全量同步導(dǎo)致數(shù)據(jù)丟失),所以這就是數(shù)據(jù)丟失的具體原因。
如何應(yīng)對(duì)腦裂
腦裂的主要原因其實(shí)就是哨兵集群認(rèn)為主節(jié)點(diǎn)已經(jīng)出現(xiàn)故障了,重新選舉其它從節(jié)點(diǎn)作為主節(jié)點(diǎn),而原主節(jié)點(diǎn)其實(shí)是假故障,從而導(dǎo)致短暫的出現(xiàn)兩個(gè)主節(jié)點(diǎn),那么在主從切換期間客戶端一旦給原主節(jié)點(diǎn)發(fā)送命令,就會(huì)造成數(shù)據(jù)丟失。
所以應(yīng)對(duì)腦裂的解決辦法應(yīng)該是去限制原主庫(kù)接收請(qǐng)求,Redis提供了兩個(gè)配置項(xiàng)。
- min-slaves-to-write:與主節(jié)點(diǎn)通信的從節(jié)點(diǎn)數(shù)量必須大于等于該值主節(jié)點(diǎn),否則主節(jié)點(diǎn)拒絕寫入。
- min-slaves-max-lag:主節(jié)點(diǎn)與從節(jié)點(diǎn)通信的ACK消息延遲必須小于該值,否則主節(jié)點(diǎn)拒絕寫入。
這兩個(gè)配置項(xiàng)必須同時(shí)滿足,不然主節(jié)點(diǎn)拒絕寫入。
在假故障期間滿足min-slaves-to-write和min-slaves-max-lag的要求,那么主節(jié)點(diǎn)就會(huì)被禁止寫入,腦裂造成的數(shù)據(jù)丟失情況自然也就解決了。
腦裂可以完全解決嗎
通過上面的學(xué)習(xí)我們知道了腦裂出現(xiàn)的場(chǎng)景,帶來的問題,以及解決辦法,那么腦裂問題可以完全被解決嗎?我們直接看下面的場(chǎng)景
為了防止腦裂我們將min-slaves-to-write設(shè)置為1,min-slaves-max-lag設(shè)置為12s,down-after-milliseconds哨兵判斷主節(jié)點(diǎn)客觀下線的限制為10s,主節(jié)點(diǎn)因?yàn)槟承┰蚩ㄗ×?5s,導(dǎo)致哨兵集群判斷主節(jié)點(diǎn)為主觀下線,主從切換,因?yàn)闆]有一個(gè)從節(jié)點(diǎn)與主節(jié)點(diǎn)之間的數(shù)據(jù)復(fù)制在12s內(nèi),這樣就規(guī)避腦裂的情況。
但是我們?cè)倏戳硗庖粋€(gè)場(chǎng)景
我們同樣將min-slaves-to-write設(shè)置為1,min-slaves-max-lag 設(shè)置為 15s,down-after-milliseconds哨兵判斷主節(jié)點(diǎn)客觀下線的限制為10s,哨兵主從切換需要 5s。主節(jié)點(diǎn)因?yàn)槟承┰蚩ㄗ×?12s,這時(shí)還會(huì)發(fā)生腦裂嗎?
主節(jié)點(diǎn)卡住12s這時(shí)哨兵集群判斷主節(jié)點(diǎn)下線,同時(shí)哨兵集群做主從切換需要5s,這就意味著主從切換過程中,主節(jié)點(diǎn)恢復(fù)運(yùn)行,而min-slaves-max-lag設(shè)置為15s那么主節(jié)點(diǎn)還是可寫,也就是說在12s~15s這期間如果有客戶端寫入原主節(jié)點(diǎn),那么這段時(shí)間的數(shù)據(jù)會(huì)丟失。
總結(jié)
Redis腦裂可以采用min-slaves-to-write和min-slaves-max-lag合理配置盡量規(guī)避,但無法徹底解決,Redis腦裂最本質(zhì)的問題是主從集群內(nèi)部沒有共識(shí)算法來維護(hù)多個(gè)節(jié)點(diǎn)的強(qiáng)一致性,它不像Zookeeper那樣,每次寫入必須大多數(shù)節(jié)點(diǎn)成功后才算成功,當(dāng)腦裂發(fā)生時(shí),Zookeeper節(jié)點(diǎn)被孤立,此時(shí)無法寫入大多數(shù)節(jié)點(diǎn),寫請(qǐng)求會(huì)直接失敗,因此Zookeeper才能保證集群的強(qiáng)一致性。