最新Redis高可用總結:Redis主從複製、哨兵集羣、腦裂

在實際的項目中,服務高可用非常重要,如,當Redis作爲緩存服務使用時, 緩解數據庫的壓力,提高數據的訪問速度,提高網站的性能 ,但如果使用Redis 是單機模式運行 ,只要一個服務器宕機就不可以提供服務,這樣會可能造成服務效率低下,甚至出現其相對應的服務應用不可用。

因此爲了實現高可用,Redis 提供了哪些高可用方案?

Redis主從複製

Redis持久化

哨兵集羣

...

Redis基於一個Master主節點多Slave從節點的模式和Redis持久化機制,將一份數據保持在多個實例中實現增加副本冗餘量,又使用哨兵機制實現主備切換, 在master故障時,自動檢測,將某個slave切換爲master,最終實現Redis高可用 。

Redis主從複製

Redis主從複製,主從庫模式一個Master主節點多Slave從節點的模式,將一份數據保存在多Slave個實例中,增加副本冗餘量,當某些出現宕機後,Redis服務還可以使用。

但是這會存在數據不一致問題,那redis的副本集是如何數據一致性?

Redis爲了保證數據副本的一致,主從庫之間採用讀寫分離的方式:

讀操作:主庫、從庫都可以執行處理;

寫操作:先在主庫執行,再由主庫將寫操作同步給從庫。  

使用讀寫分離方式的好處,可以避免當主從庫都可以處理寫操作時,主從庫處理寫操作加鎖等一系列鉅額的開銷。

採用讀寫分離方式,寫操作只會在主庫中進行後同步到從庫中,那主從庫是如何同步數據的呢?

主從庫是同步數據方式有兩種:

全量同步:通常是主從服務器剛剛連接的時候,會先進行全量同步

增量同步 :一般在全同步結束後,進行增量同步,比如主從庫間網絡斷開後,再進行數據同步。

全量同步

主從庫間第一次全量同步,具體分成三個階段:

當一個從庫啓動時,從庫給主庫發送 psync 命令進行數據同步(psync 命令包含:主庫的 runID 和複製進度 offset 兩個參數),

當主庫接收到psync 命令後將會保存RDB 文件併發送給從庫,發送期間會使用緩存區(replication buffer)記錄後續的所有寫操作 ,從庫收到數據後,會先清空當前數據庫,然後加載從主庫獲取的RDB 文件,

當主庫完成 RDB 文件發送後,也會把將保存發送RDB文件期間寫操作的replication buffer發給從庫,從庫再重新執行這些操作。這樣一來,主從庫就實現同步了。

另外,爲了分擔主庫生成 RDB 文件和傳輸 RDB 文件壓力,提高效率,可以使用 “主 - 從 - 從”模式將主庫生成 RDB 和傳輸 RDB 的壓力,以級聯的方式分散到從庫上。

增量同步

增量同步,基於環形緩衝區repl_backlog_buffer緩存區實現。

在環形緩衝區,主庫會記錄自己寫到的位置 master_repl_offset ,從庫則會記錄自己已經讀到的位置slave_repl_offset, 主庫並通過master_repl_offset 和 slave_repl_offset的差值的數據同步到從庫。

主從庫間網絡斷了, 主從庫會採用增量複製的方式繼續同步,主庫會把斷連期間收到的寫操作命令,寫入 replication buffer,同時也會把這些操作命令也寫入 repl_backlog_buffer 這個緩衝區,然後主庫並通過master_repl_offset 和 slave_repl_offset的差值數據同步到從庫。

因爲repl_backlog_buffer 是一個環形緩衝區,當在緩衝區寫滿後,主庫會繼續寫入,此時,會出現什麼情況呢?

覆蓋掉之前寫入的操作。如果從庫的讀取速度比較慢,就有可能導致從庫還未讀取的操作被主庫新寫的操作覆蓋了,這會導致主從庫間的數據不一致。因此需要關注 repl_backlog_size參數,調整合適的緩衝空間大小,避免數據覆蓋,主從數據不一致。

主從複製,除了會出現數據不一致外,甚至可能出現主庫宕機的情況,Redis會有主從自主切換機制,那如何實現的呢?

Redis哨兵機制

當主庫掛了,redis寫操作和數據同步無法進行,爲了避免這樣情況,可以在主庫掛了後重新在從庫中選舉出一個新主庫,並通知到客戶端,redis提供了 哨兵機制,哨兵爲運行在特殊模式下的 Redis 進程。

Redis會有主從自主切換機制,那如何實現的呢?

哨兵機制是實現主從庫自動切換的關鍵機制,其主要分爲三個階段:

監控:哨兵進程會週期性地給所有的主從庫發送 PING 命令,檢測它們是否仍然在線運行。

選主(選擇主庫):主庫掛了以後,哨兵基於一定規則評分選選舉出一個從庫實例新的主庫 。

通知 :哨兵會將新主庫的信息發送給其他從庫,讓它們和新主庫建立連接,並進行數據複製。同時,哨兵會把新主庫的信息廣播通知給客戶端,讓它們把請求操作發到新主庫上。

其中,在監控中如何判斷主庫是否處於下線狀態?

哨兵對主庫的下線判斷分爲:

主觀下線:哨兵進程會使用 PING 命令檢測它自己和主、從庫的網絡連接情況,用來判斷實例的狀態, 如果單哨兵發現主庫或從庫對 PING 命令的響應超時了,那麼,哨兵就會先把它標記爲“主觀下線”

客觀下線:在哨兵集羣中,基於少數服從多數,多數實例都判定主庫已“主觀下線”,則認爲主庫“客觀下線”。

爲什麼會有這兩種"主觀下線"和“客觀下線”的下線狀態呢?

由於單機哨兵很容易產生誤判,誤判後主從切換會產生一系列的額外開銷,爲了減少誤判,避免這些不必要的開銷,採用哨兵集羣,引入多個哨兵實例一起來判斷,就可以避免單個哨兵因爲自身網絡狀況不好,而誤判主庫下線的情況,

基於少數服從多數原則, 當有 N 個哨兵實例時,最好要有 N/2 + 1 個實例判斷主庫爲“主觀下線”,才能最終判定主庫爲“客觀下線” (可以自定義設置闕值)。

那麼哨兵之間是如何互相通信的呢?

哨兵集羣中哨兵實例之間可以相互發現,基於 Redis 提供的發佈 / 訂閱機制(pub/sub 機制),

哨兵可以在主庫中發佈/訂閱消息,在主庫上有一個名爲“\__sentinel__:hello”的頻道,不同哨兵就是通過它來相互發現,實現互相通信的,而且只有訂閱了同一個頻道的應用,才能通過發佈的消息進行信息交換。

哨兵 1連接相關信息(IP端口)發佈到“\__sentinel__:hello”頻道上,哨兵 2 和 3 訂閱了該頻道。

哨兵 2 和 3 就可以從這個頻道直接獲取哨兵 1連接信息,以這樣的方式哨兵集羣就形成了,實現各個哨兵互相通信。

哨兵集羣中各個實現通信後,就可以判定主庫是否已客觀下線。

在已判定主庫已下線後,又如何選舉出新的主庫?

新主庫選舉按照一定條件篩選出的符合條件的從庫,並按照一定規則對其進行打分,最高分者爲新主庫。

通常一定條件包括:

從庫的當前在線狀態,

判斷它之前的網絡連接狀態,通過down-after-milliseconds * num(斷開連接次數),當斷開連接次數超過閾值,不適合爲新主庫。

一定規則包括

從庫優先級 , 通過slave-priority 配置項,給不同的從庫設置不同優先級,優先級最高的從庫得分高

從庫複製進度,和舊主庫同步程度最接近的從庫得分高,通過repl_backlog_buffer緩衝區記錄主庫 master_repl_offset 和從庫slave_repl_offset 相差最小高分

從庫 ID 號 , ID 號小的從庫得分高。

全都都基於在只有在一定規則中的某一輪評出最高分從庫就選舉結束,哨兵發起主從切換

leader哨兵

選舉完新的主庫後,不能每個哨兵都發起主從切換,需要選舉成leader哨兵,那如何選舉leader哨兵執行主從切換?

選舉leader哨兵,也是基於少數服從多數原則"投票仲裁"選舉出來,

當任何一個從庫判定主庫“主觀下線”後,發送命令 s-master-down-by-addr命令發送想要成爲Leader的信號,

其他哨兵根據與主機連接情況作出相對的響應,贊成票Y,反對票N,而且如果有多個哨兵發起請求,每個哨兵的贊成票只能投給其中一個,其他只能爲反對票。

想要成爲Leader 的哨兵,要滿足兩個條件:

第一,獲得半數以上的贊成票;

第二,獲得的票數同時還需要大於等於哨兵配置文件中的quorum值。

選舉完leader哨兵並新主庫切換完畢之後,那麼leader哨兵怎麼通知客戶端?

還是基於哨兵自身的 pub/sub 功能,實現了客戶端和哨兵之間的事件通知,客戶端訂閱哨兵自身消息頻道 ,而且哨兵提供的消息訂閱頻道有很多,不同頻道包含了:

其中,當客戶端從哨兵訂閱消息主從庫切換,當主庫切換後,端戶端就會接收到新主庫的連接信息:

switch-master <master name> <oldip> <oldport> <newip> <newport>  

在這樣的方式哨兵就可以通知客戶端切換了新庫。

基於上述的機制和原理Redis實現了高可用,但也會帶了一些潛在的風險,比如數據缺失。

數據問題

Redis實現高可用,但實現期間可能產出一些風險:

主備切換的過程, 異步複製導致的數據丟失

腦裂導致的數據丟失

主備切換的過程,異步複製導致數據不一致

數據丟失-主從異步複製

因爲master 將數據複製給slave是異步實現的,在複製過程中,這可能存在master有部分數據還沒複製到slave,master就宕機了,此時這些部分數據就丟失了。

總結:主庫的數據還沒有同步到從庫,結果主庫發生了故障,未同步的數據就丟失了。

數據丟失-腦裂

何爲腦裂?當一個集羣中的 master 恰好網絡故障,導致與 sentinal 通信不上了,sentinal會認爲master下線,且sentinal選舉出一個slave 作爲新的 master,此時就存在兩個 master了。

此時,可能存在client還沒來得及切換到新的master,還繼續寫向舊master的數據,當master再次恢復的時候,會被作爲一個slave掛到新的master 上去,自己的數據將會清空,重新從新的master 複製數據,這樣就會導致數據缺失。

總結:主庫的數據還沒有同步到從庫,結果主庫發生了故障,等從庫升級爲主庫後,未同步的數據就丟失了。

數據丟失解決方案

數據丟失可以通過合理地配置參數 min-slaves-to-write 和 min-slaves-max-lag 解決,比如

min-slaves-to-write 1

min-slaves-max-lag 10

如上兩個配置:要求至少有 1 個 slave,數據複製和同步的延遲不能超過 10 秒,如果超過 1 個 slave,數據複製和同步的延遲都超過了 10 秒鐘,那麼這個時候,master 就不會再接收任何請求了。

數據不一致

在主從異步複製過程,當從庫因爲網絡延遲或執行復雜度高命令阻塞導致滯後執行同步命令,這樣就會導致數據不一致

解決方案:可以開發一個外部程序來監控主從庫間的複製進度(master_repl_offset 和 slave_repl_offset ),通過監控 master_repl_offset 與slave_repl_offset差值得知複製進度,當複製進度不符合預期設置的Client不再從該從庫讀取數據。


總結

Redis使用主從複製、持久化、哨兵機制等實現高可用,需要理解其實現過程,也要明白其帶了風險以及解決方案,才能在實際項目更好優化,提升系統的可靠性、穩定性。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章