Redis集群之脑裂:一次奇怪的数据丢失
Redis集群之腦裂:一次奇怪的數據丟失
- 引言
- 為什么會發生腦裂
- 第一步:確認是不是數據同步出現了問題
- 第二步:排查客戶端的操作日志,發現腦裂現象
- 第三步:發現是原主庫假故障導致的腦裂
- 為什么腦裂會導致數據丟失
- 如何應對腦裂問題
引言
所謂的腦裂,就是指在主從集群中,同時有兩個主節點,它們都能接收寫請求。而腦裂最直接的影響,就是客戶端不知道應該往哪個主節點寫入數據,結果就是不同的客戶端會往不同的主節點上寫入數據。而且,嚴重的話,腦裂會進一步導致數據丟失。
為什么會發生腦裂
第一步:確認是不是數據同步出現了問題
在主從集群中發生數據丟失,最常見的原因就是主庫的數據還沒有同步到從庫,結果主庫發生了故障,等從庫升級為主庫后,未同步的數據就丟失了。
新寫入主庫的數據 a:1、b:3,就因為在主庫故障前未同步到從庫而丟失了。
如果是這種情況的數據丟失,我們可以通過比對主從庫上的復制進度差值來進行判斷,也就是計算 master_repl_offset 和 slave_repl_offset 的差值。如果從庫上的slave_repl_offset 小于原主庫的 master_repl_offset,那么就可以認定數據丟失是由數據同步未完成導致的。
當我們發現數據丟失后,我們檢查了新主庫升級前的slave_repl_offset,以及原主庫的 master_repl_offset,它們是一致的,也就是說,這個升級為新主庫的從庫,在升級時已經和原主庫的數據保持一致了。那么,為什么還會出現客戶端發送的數據丟失呢?
第二步:排查客戶端的操作日志,發現腦裂現象
在排查客戶端的操作日志時發現,在主從切換后的一段時間內,有一個客戶端仍然在和原主庫通信,并沒有和升級的新主庫進行交互。這就相當于主從集群中同時有了兩個主庫。根據這個跡象,我們就想到了在分布式主從集群發生故障時會出現的一個問題:腦裂。
不同客戶端給兩個主庫發送數據寫操作,按道理來說,只會導致新數據會分布在不同的主庫上,并不會造成數據丟失。那么,為什么我們的數據仍然丟失了呢?
第三步:發現是原主庫假故障導致的腦裂
采用哨兵機制進行主從切換的,當主從切換發生時,一定是有超過預設數量(quorum 配置項)的哨兵實例和主庫的心跳都超時了,才會把主庫判斷為客觀下線,然后,哨兵開始執行切換操作。哨兵切換完成后,客戶端會和新主庫進行通信,發送請求操作。
在切換過程中,既然客戶端仍然和原主庫通信,這就表明,原主庫并沒有真的發生故障(例如主庫進程掛掉)。我們猜測,主庫是由于某些原因無法處理請求,也沒有響應哨兵的心跳,才被哨兵錯誤地判斷為客觀下線的。結果,在被判斷下線之后,原主庫又重新開始處理請求了,而此時,哨兵還沒有完成主從切換,客戶端仍然可以和原主庫通信,客戶端發送的寫操作就會在原主庫上寫入數據了。
的確,我們看到原主庫所在的機器有一段時間的 CPU 利用率突然特別高,這是我們在機器上部署的一個數據采集程序導致的。因為這個程序基本把機器的 CPU 都用滿了,導致Redis 主庫無法響應心跳了,在這個期間內,哨兵就把主庫判斷為客觀下線,開始主從切換了。不過,這個數據采集程序很快恢復正常,CPU 的使用率也降下來了。此時,原主庫又開始正常服務請求了。
正因為原主庫并沒有真的發生故障,我們在客戶端操作日志中就看到了和原主庫的通信記錄。等到從庫被升級為新主庫后,主從集群里就有兩個主庫了,到這里,我們就把腦裂發生的原因摸清楚了。
為什么腦裂會導致數據丟失
主從切換后,從庫一旦升級為新主庫,哨兵就會讓原主庫執行 slave of 命令,和新主庫重新進行全量同步。而在全量同步執行的最后階段,原主庫需要清空本地的數據,加載新主庫發送的 RDB 文件,這樣一來,原主庫在主從切換期間保存的新寫數據就丟失了。
在主從切換的過程中,如果原主庫只是“假故障”,它會觸發哨兵啟動主從切換,一旦等它從假故障中恢復后,又開始處理請求,這樣一來,就會和新主庫同時存在,形成腦裂。等到哨兵讓原主庫和新主庫做全量同步后,原主庫在切換期間保存的數據就丟失了。
如何應對腦裂問題
剛剛說了,主從集群中的數據丟失事件,歸根結底是因為發生了腦裂。所以,我們必須要找到應對腦裂問題的策略。
既然問題是出在原主庫發生假故障后仍然能接收請求上,我們就開始在主從集群機制的配置項中查找是否有限制主庫接收請求的設置。
Redis 已經提供了兩個配置項來限制主庫的請求處理,分別是 minslaves-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-slavesmax-lag 的組合要求就無法得到滿足,原主庫就會被限制接收客戶端請求,客戶端也就不能在原主庫中寫入新數據了。
等到新主庫上線時,就只有新主庫能接收和處理客戶端請求,此時,新寫的數據會被直接寫到新主庫中。而原主庫會被哨兵降為從庫,即使它的數據被清空了,也不會有新數據丟失。
覺得有用的客官可以點贊、關注下!感謝支持🙏謝謝!
總結
以上是生活随笔為你收集整理的Redis集群之脑裂:一次奇怪的数据丢失的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 高职高考数学可以用计算机吗,将高职高考数
- 下一篇: 学Dapr Actors 看这篇就够了