Redis 主從複製(主備切換,哨兵機制)

redis集羣原理及搭建與使用:https://juejin.im/post/5ad54d76f265da23970759d3

一、主從架構

在Redis中,用戶可以通過執行SALVEOF命令或者設置salveof選項,讓一個服務器去複製(replicate)另一個服務器,我們稱呼被複制的服務器爲主服務器(master),而對主服務器進行復制的服務器則被稱爲從服務器(salve)

1. 主從架構形式

  1. 主服務器負責接收寫請求
  2. 從服務器負責接收讀請求
  3. 從服務器的數據由主服務器複製過去。主從服務器的數據是一致的
    在這裏插入圖片描述
    主從架構除了上面的形式,也有下面這種的(只不過用得比較少):
    在這裏插入圖片描述

2. 主從架構優點

  1. 讀寫分離(主服務器負責寫,從服務器負責讀)
  2. 高可用(某一臺從服務器掛了,其他從服務器還能繼續接收請求,不影響服務)
  3. 處理更多的併發量(每臺從服務器都可以接收讀請求,讀QPS就上去了)

二、複製功能

在這裏插入圖片描述

1. 複製功能的具體實現

複製功能分爲兩個操作:

  • 同步(sync)

將從服務器的數據庫狀態更新至主服務器的數據庫狀態

  • 命令傳播(command propagate)

主服務器的數據庫狀態被修改,導致主從服務器的數據庫狀態不一致,讓主從服務器的數據庫狀態重新回到一致狀態。
在這裏插入圖片描述
從服務器對主服務器的同步又可以分爲兩種情況:

  1. 初次同步:從服務器沒有複製過任何的主服務器,或者從服務器要複製的主服務器跟上次複製的主服務器不一樣。
  2. 斷線後同步:處於命令傳播階段的主從服務器因爲網絡原因中斷了複製,從服務器通過自動重連重新連接主服務器,並繼續複製主服務器
    在Redis2.8以前,斷線後複製這部分其實缺少的只是部分的數據,但是要讓主從服務器重新執行SYNC命令,這樣的做法是非常低效的。(因爲執行SYNC命令是把所有的數據再次同步,而不是隻同步丟失的數據)

接下來我們來詳細看看Redis2.8以後複製功能是怎麼實現的:

2. 複製的前置工作

  1. 從服務器設置主服務器的IP和端口
  2. 建立與主服務器的Socket連接
  3. 發送PING命令(檢測Socket讀寫是否正常與主服務器的通信狀況)
  4. 身份驗證(看有沒有設置對應的驗證配置)
  5. 從服務器給主服務器發送端口的信息,主服務器記錄監聽的端口
    在這裏插入圖片描述
    前面也提到了,Redis2.8之前,斷線後同步會重新執行SYNC命令,這是非常低效的。下面我們來看一下Redis2.8之後是怎麼進行同步的。

Redis從2.8版本開始,使用PSYNC命令來替代SYNC命令執行復制時同步的操作。
PSYNC命令具有完整重同步和部分重同步兩種模式(其實就跟上面所說的初次複製和斷線後複製差不多個意思)。

3. 完整重同步

  1. 從服務器向主服務器發送PSYNC命令
  2. 收到PSYNC命令的主服務器執行BGSAVE命令,在後臺生成一個RDB文件。並用一個緩衝區來記錄從現在開始執行的所有寫命令。
  3. 當主服務器的BGSAVE命令執行完後,將生成的RDB文件發送給從服務器,從服務器接收和載入RBD文件。將自己的數據庫狀態更新至與主服務器執行BGSAVE命令時的狀態。
  4. 主服務器將所有緩衝區的寫命令發送給從服務器,從服務器執行這些寫命令,達到數據最終一致性。
    在這裏插入圖片描述

4. 部分重同步

接下來我們來看看部分重同步,部分重同步可以讓我們斷線後重連只需要同步缺失的數據(而不是Redis2.8之前的同步全部數據),這是符合邏輯的!
部分重同步功能由以下部分組成:

  1. 主從服務器的複製偏移量
  2. 主服務器的複製積壓緩衝區
  3. 服務器運行的ID(run ID)
    首先我們來解釋一下上面的名詞:

複製偏移量:執行復制的雙方都會分別維護一個複製偏移量

  • 主服務器每次傳播N個字節,就將自己的複製偏移量加上N
  • 從服務器每次收到主服務器的N個字節,就將自己的複製偏移量加上N

通過對比主從複製的偏移量,就很容易知道主從服務器的數據是否處於一致性的狀態!
在這裏插入圖片描述
那斷線重連以後,從服務器向主服務器發送PSYNC命令,報告現在的偏移量是36,那麼主服務器該對從服務器執行完整重同步還是部分重同步呢??這就交由複製積壓緩衝區來決定。
當主服務器進行命令傳播時,不僅僅會將寫命令發送給所有的從服務器,還會將寫命令入隊到複製積壓緩衝區裏面(這個大小可以調的)。如果複製積壓緩衝區存在丟失的偏移量的數據,那就執行部分重同步,否則執行完整重同步。

服務器運行的ID(run ID)實際上就是用來比對ID是否相同。如果不相同,則說明從服務器斷線之前複製的主服務器和當前連接的主服務器是兩臺服務器,這就會進行完整重同步。
在這裏插入圖片描述

5. 命令傳播

當完成了同步之後,主從服務器就會進入命令傳播階段。這時主服務器只要將自己的寫命令發送給從服務器,而從服務器接收並執行主服務器發送過來的寫命令,就可以保證主從服務器一直保持數據一致了!
在命令傳播階段,從服務器默認會以每秒一次的頻率,向服務器發送命令REPLCONF ACK <replication_offset> 其中replication_offset是從服務器當前的複製偏移量
發送這個命令主要有三個作用:

  • 檢測主從服務器的網絡狀態
  • 輔助實現min-slaves選項
  • 檢測命令丟失

三、主備切換(故障轉移)

1. 哨兵(Sentinal)機制

Redis提供了哨兵(Sentinal)機制。如果主服務器掛了,我們可以將從服務器升級爲主服務器,等到舊的主服務器(掛掉的那個)重連上來,會將它(掛掉的主服務器)變成從服務器。

  • 這個過程叫做主備切換(故障轉移)

哨兵(Sentinal)機制主要用於實現Redis的高可用性,主要的功能如下:

  1. Sentinel不停地監控Redis主從服務器是否正常工作
  2. 如果某個Redis實例有故障,那麼哨兵負責發送消息通知管理員
  3. 如果主服務器掛掉了,會自動將從服務器提升爲主服務器(包括配置都會修改)。
  4. Sentinel可以作爲配置中心,能夠提供當前主服務器的信息。

tips:Sentinel可以讓我們的Redis實現高可用,Sentinel作爲這麼一個組件,自身也必然是高可用的(不可能是單點的)

下面來具體講講Sentinel是如何將從服務器提升爲主服務器的。

2. 啓動和初始化哨兵 Sentinel

首先我們要知道的是:Sentinel本質上只是一個運行在特殊模式下的Redis服務器。因爲Sentinel做的事情和Redis服務器是不一樣的,所以它們的初始化是有所區別的(比如,Sentinel在初始化的時候並不會載入AOF/RDB文件,因爲Sentinel根本就不用數據庫)。

然後,在啓動的時候會將普通Redis服務器的代碼替換成Sentinel專用代碼。(所以Sentinel雖然作爲Redis服務器,但是它不能執行SET、DBSIZE等等命令,因爲命令表的代碼被替換了)

接着,初始化Sentinel的狀態,並根據給定的配置文件初始化Sentinel監視的主服務器列表。

最後,Sentinel會創建兩個連向主服務器的網絡連接:

  • 命令連接(發送和接收命令)
  • 訂閱連接(訂閱主服務器的_sentinel_:hello頻道)

3. 獲取和更新信息

Sentinel通過主服務器發送INFO命令來獲得主服務器屬下所有從服務器的地址信息,併爲這些從服務器創建相應的實例結構。當發現有新的從服務器出現時,除了創建對應的從服務器實例結構,Sentinel還會創建命令連接和訂閱連接。
在這裏插入圖片描述
在Sentinel運行的過程中,通過命令連接會以每兩秒一次的頻率向監視的主從服務器的_sentinel_:hello頻道發送命令(主要發送Sentinel本身的信息,監聽主從服務器的信息),並通過訂閱連接接收_sentinel_:hello頻道的信息。

這樣一來一回,我們就可以更新每個Sentinel實例結構的信息。

4. 判斷主服務器是否下線了

判斷主服務器是否下線有兩種情況:

  1. 主觀下線
  • Sentinel會以每秒一次的頻率向與它創建命令連接的實例(包括主從服務器和其他的Sentinel)發送PING命令,通過PING命令返回的信息判斷實例是否在線
  • 如果一個主服務器在down-after-milliseconds毫秒內連續向Sentinel發送無效回覆,那麼當前Sentinel就會主觀認爲該主服務器已經下線了。
  1. 客觀下線
  • 當Sentinel將一個主服務器判斷爲主觀下線以後,爲了確認該主服務器是否真的下線,它會向同樣監視該主服務器的Sentinel詢問,看它們是否也認爲該主服務器是否下線。
  • 如果足夠多的Sentinel認爲該主服務器是下線的,那麼就判定該主服務爲客觀下線,並對主服務器執行故障轉移操作。

在多少毫秒內無效回覆才認定主服務器是主觀下線的,以及有多少個Sentinel認爲主服務器是下線才認定爲客觀下線。這都是可以配置的

5. 選舉領頭Sentinel和故障轉移

當一個主服務器認爲爲客觀下線以後,監視這個下線的主服務器的各種Sentinel會進行協商,選舉出一個領頭的Sentinel,領頭的Sentinel會對下線的主服務器執行故障轉移操作。

選舉領頭Sentinel的規則也比較多,總的來說就是先到先得(哪個快,就選哪個)
選舉出領頭的Sentinel之後,領頭的Sentinel會對已下線的主服務器執行故障轉移操作,包括三個步驟:

  1. 在已下線主服務器屬下的從服務器中,挑選一個轉換爲主服務器
  2. 讓已下線主服務器屬下的所有從服務器改爲複製新的主服務器
  3. 已下線的主服務器重新連接時,讓他成爲新的主服務器的從服務器

挑選某一個從服務器作爲主服務器也是有策略的,大概如下:
4. 跟master斷開連接的時長
5. slave優先級
6. 複製offset
7. run id

6. 丟失數據

目前爲止的主從+哨兵架構可以說Redis是高可用的,但要清楚的是:Redis還是會丟失數據的
丟失數據有兩種情況:

  1. 異步複製導致的數據丟失
  • 有部分數據還沒複製到從服務器,主服務器就宕機了,此時這些部分數據就丟失了
  1. 腦裂導致的數據丟失
  • 有時候主服務器脫離了正常網絡,跟其他從服務器不能連接。此時哨兵可能就會認爲主服務器下線了(然後開啓選舉,將某個從服務器切換成了主服務器),但是實際上主服務器還運行着。這個時候,集羣裏就會有兩個服務器(也就是所謂的腦裂)。
  • 雖然某個從服務器被切換成了主服務器,但是可能客戶端還沒來得及切換到新的主服務器,客戶端還繼續寫向舊主服務器寫數據。舊的服務器重新連接時,會作爲從服務器複製新的主服務器(這意味着舊數據丟失)。
    可以通過以下兩個配置儘量減少數據丟失的可能:
min-slaves-to-write 1
min-slaves-max-lag 10
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章