石景山區(qū)城鄉(xiāng)建設(shè)委員會(huì)網(wǎng)站百度推廣入口官網(wǎng)
集群腦裂導(dǎo)致數(shù)據(jù)丟失怎么辦?
什么是腦裂?
先來理解集群的腦裂現(xiàn)象,這就好比一個(gè)人有兩個(gè)大腦,那么到底受誰控制呢?
那么在 Redis 中,集群腦裂產(chǎn)生數(shù)據(jù)丟失的現(xiàn)象是怎樣的呢?
在 Redis 主從架構(gòu)中,部署方式一般是「一主多從」,主節(jié)點(diǎn)提供寫操作,從節(jié)點(diǎn)提供讀操作。 如果主節(jié)點(diǎn)的網(wǎng)絡(luò)突然發(fā)生了問題,它與所有的從節(jié)點(diǎn)都失聯(lián)了,但是此時(shí)的主節(jié)點(diǎn)和客戶端的網(wǎng)絡(luò)是正常的,這個(gè)客戶端并不知道 Redis 內(nèi)部已經(jīng)出現(xiàn)了問題,還在照樣的向這個(gè)失聯(lián)的主節(jié)點(diǎn)寫數(shù)據(jù)(過程A),此時(shí)這些數(shù)據(jù)被舊主節(jié)點(diǎn)緩存到了緩沖區(qū)里,因?yàn)橹鲝墓?jié)點(diǎn)之間的網(wǎng)絡(luò)問題,這些數(shù)據(jù)都是無法同步給從節(jié)點(diǎn)的。
這時(shí),哨兵也發(fā)現(xiàn)主節(jié)點(diǎn)失聯(lián)了,它就認(rèn)為主節(jié)點(diǎn)掛了(但實(shí)際上主節(jié)點(diǎn)正常運(yùn)行,只是網(wǎng)絡(luò)出問題了),于是哨兵就會(huì)在「從節(jié)點(diǎn)」中選舉出一個(gè) leader 作為主節(jié)點(diǎn),這時(shí)集群就有兩個(gè)主節(jié)點(diǎn)了 ——?腦裂出現(xiàn)了。
然后,網(wǎng)絡(luò)突然好了,哨兵因?yàn)橹耙呀?jīng)選舉出一個(gè)新主節(jié)點(diǎn)了,它就會(huì)把舊主節(jié)點(diǎn)降級(jí)為從節(jié)點(diǎn)(A),然后從節(jié)點(diǎn)(A)會(huì)向新主節(jié)點(diǎn)請(qǐng)求數(shù)據(jù)同步,因?yàn)榈谝淮瓮绞侨客降姆绞?#xff0c;此時(shí)的從節(jié)點(diǎn)(A)會(huì)清空掉自己本地的數(shù)據(jù),然后再做全量同步。所以,之前客戶端在過程 A 寫入的數(shù)據(jù)就會(huì)丟失了,也就是集群產(chǎn)生腦裂數(shù)據(jù)丟失的問題。
總結(jié)一句話就是:由于網(wǎng)絡(luò)問題,集群節(jié)點(diǎn)之間失去聯(lián)系。主從數(shù)據(jù)不同步;重新平衡選舉,產(chǎn)生兩個(gè)主服務(wù)。等網(wǎng)絡(luò)恢復(fù),舊主節(jié)點(diǎn)會(huì)降級(jí)為從節(jié)點(diǎn),再與新主節(jié)點(diǎn)進(jìn)行同步復(fù)制的時(shí)候,由于會(huì)從節(jié)點(diǎn)會(huì)清空自己的緩沖區(qū),所以導(dǎo)致之前客戶端寫入的數(shù)據(jù)丟失了。
解決方案
當(dāng)主節(jié)點(diǎn)發(fā)現(xiàn)從節(jié)點(diǎn)下線或者通信超時(shí)的總數(shù)量小于閾值時(shí),那么禁止主節(jié)點(diǎn)進(jìn)行寫數(shù)據(jù),直接把錯(cuò)誤返回給客戶端。
在 Redis 的配置文件中有兩個(gè)參數(shù)我們可以設(shè)置:
- min-slaves-to-write x,主節(jié)點(diǎn)必須要有至少 x 個(gè)從節(jié)點(diǎn)連接,如果小于這個(gè)數(shù),主節(jié)點(diǎn)會(huì)禁止寫數(shù)據(jù)。
- min-slaves-max-lag x,主從數(shù)據(jù)復(fù)制和同步的延遲不能超過 x 秒,如果超過,主節(jié)點(diǎn)會(huì)禁止寫數(shù)據(jù)。
我們可以把 min-slaves-to-write 和 min-slaves-max-lag 這兩個(gè)配置項(xiàng)搭配起來使用,分別給它們?cè)O(shè)置一定的閾值,假設(shè)為 N 和 T。
這兩個(gè)配置項(xiàng)組合后的要求是,主庫連接的從庫中至少有 N 個(gè)從庫,和主庫進(jìn)行數(shù)據(jù)復(fù)制時(shí)的 ACK 消息延遲不能超過 T 秒,否則,主庫就不會(huì)再接收客戶端的寫請(qǐng)求了。
即使原主庫是假故障,它在假故障期間也無法響應(yīng)哨兵心跳,也不能和從庫進(jìn)行同步,自然也就無法和從庫進(jìn)行 ACK 確認(rèn)了。這樣一來,min-slaves-to-write 和 min-slaves-max-lag 的組合要求就無法得到滿足,原主庫就會(huì)被限制接收客戶端寫請(qǐng)求,客戶端也就不能在原主庫中寫入新數(shù)據(jù)了。
等到新主庫上線時(shí),就只有新主庫能接收和處理客戶端請(qǐng)求,此時(shí),新寫的數(shù)據(jù)會(huì)被直接寫到新主庫中。而原主庫會(huì)被哨兵降為從庫,即使它的數(shù)據(jù)被清空了,也不會(huì)有新數(shù)據(jù)丟失。
再來舉個(gè)例子。
假設(shè)我們將 min-slaves-to-write 設(shè)置為 1,把 min-slaves-max-lag 設(shè)置為 12s,把哨兵的 down-after-milliseconds 設(shè)置為 10s,主庫因?yàn)槟承┰蚩ㄗ×?15s,導(dǎo)致哨兵判斷主庫客觀下線,開始進(jìn)行主從切換。
同時(shí),因?yàn)樵鲙炜ㄗ×?15s,沒有一個(gè)從庫能和原主庫在 12s 內(nèi)進(jìn)行數(shù)據(jù)復(fù)制,原主庫也無法接收客戶端請(qǐng)求了。
這樣一來,主從切換完成后,也只有新主庫能接收請(qǐng)求,不會(huì)發(fā)生腦裂,也就不會(huì)發(fā)生數(shù)據(jù)丟失的問題了