摘抄自:https://zhuanlan.zhihu.com/p/308534431
如何防止Redis腦裂導致數據丟失?
所謂的腦裂,就是指在主從集群中,同時有兩個主節點,它們都能接收寫請求。而腦裂最直接的影響,就是客戶端不知道應該往哪個主節點寫入數據,結果就是不同的客戶端會往不同的主節點上寫入數據。而且,嚴重的話,腦裂會進一步導致數據丟失。
為什么會發生腦裂?
1.確認是不是數據同步出現了問題
在主從集群中發生數據丟失,最常見的原因就是主庫的數據還沒有同步到從庫,結果主庫發生了故障,等從庫升級為主庫后,未同步的數據就丟失了。
如果是這種情況的數據丟失,我們可以通過比對主從庫上的復制進度差值來進行判斷,也就是計算 master_repl_offset 和 slave_repl_offset 的差值。如果從庫上的 slave_repl_offset 小於原主庫的 master_repl_offset,那么,我們就可以認定數據丟失是由數據同步未完成導致的。
2.排查客戶端的操作日志,發現腦裂現象
在排查客戶端的操作日志時,我們發現,在主從切換后的一段時間內,有一個客戶端仍然在和原主庫通信,並沒有和升級的新主庫進行交互。這就相當於主從集群中同時有了兩個主庫。根據這個跡象,我們就想到了在分布式主從集群發生故障時會出現的一個問題:腦裂。
但是,不同客戶端給兩個主庫發送數據寫操作,按道理來說,只會導致新數據會分布在不同的主庫上,並不會造成數據丟失。那么,為什么我們的數據仍然丟失了呢?
3.發現是原主庫假故障導致的腦裂
我們是采用哨兵機制進行主從切換的,當主從切換發生時,一定是有超過預設數量(quorum 配置項)的哨兵實例和主庫的心跳都超時了,才會把主庫判斷為客觀下線,然后,哨兵開始執行切換操作。哨兵切換完成后,客戶端會和新主庫進行通信,發送請求操作。
但是,在切換過程中,既然客戶端仍然和原主庫通信,這就表明,原主庫並沒有真的發生故障(例如主庫進程掛掉)。
為什么腦裂會導致數據丟失?
主從切換后,從庫一旦升級為新主庫,哨兵就會讓原主庫執行 slave of 命令,和新主庫重新進行全量同步。而在全量同步執行的最后階段,原主庫需要清空本地的數據,加載新主庫發送的 RDB 文件,這樣一來,原主庫在主從切換期間保存的新寫數據就丟失了。
如何應對腦裂問題?
Redis 已經提供了兩個配置項來限制主庫的請求處理,分別是 min-slaves-to-write 和 min-slaves-max-lag。
min-slaves-to-write:這個配置項設置了主庫能進行數據同步的最少從庫數量; min-slaves-max-lag:這個配置項設置了主從庫間進行數據復制時,從庫給主庫發送 ACK 消息的最大延遲(以秒為單位)。 我們可以把 min-slaves-to-write 和 min-slaves-max-lag 這兩個配置項搭配起來使用,分別給它們設置一定的閾值,假設為 N 和 T。這兩個配置項組合后的要求是,主庫連接的從庫中至少有 N 個從庫,和主庫進行數據復制時的 ACK 消息延遲不能超過 T 秒,否則,主庫就不會再接收客戶端的請求了。
即使原主庫是假故障,它在假故障期間也無法響應哨兵心跳,也不能和從庫進行同步,自然也就無法和從庫進行 ACK 確認了。這樣一來,min-slaves-to-write 和 min-slaves-max-lag 的組合要求就無法得到滿足,原主庫就會被限制接收客戶端請求,客戶端也就不能在原主庫中寫入新數據了。