天天看點

深入分析Redis的主從複制機制

深入分析Redis的主從複制機制

最近由于疫情影響,時間比較多,是以開始學習之前一直想學,但是卻沒時間學的Redis。這兩天研究了一下Redis的持久化以及主從複制機制,現在已經很晚了,就不多廢話了。這篇部落格就來談一談Redis的主從複制機制。在這裡需要提醒一下,主從複制依賴于Redis的快照持久化(RDB),是以如果不了解持久化,請先去研究那一塊的内容,可以看看這篇部落格:詳細分析Redis的持久化操作—RDB與AOF。

2|0二、正文

2|12.1 什麼是主從複制

首先我們來談一談最基本的問題——什麼是主從複制,為什麼需要它?我們知道,現在的應用基本上都會使用叢集進行部署,同一個應用部署在多台伺服器上,各台伺服器互相同步,各自承擔一部分任務,以此來減輕單台伺服器的壓力。而主從複制就是Redis用來對存儲相同資料的多台伺服器進行同步的機制。

假設我們隻在一台伺服器上部署了Redis伺服器,那所有需要通路Redis伺服器的請求,都需要這一台伺服器來處理,這對伺服器來說有很大的壓力。如果通路的很頻繁,那麼一台伺服器根本處理不過來,是以我們需要多台伺服器同時部署Redis,然後每一台伺服器承擔一部分任務。

如果我們部署了多台Redis伺服器,存儲相同的資料,為同一個應用進行服務,那麼不難想到,我們需要處理一個問題——資料同步。我們必須保證這多台伺服器的Redis資料庫中,存儲的資料是一緻的,而且都應該是正确的資料,否則将會導緻對請求進行錯誤的處理,比如查詢出的是過期的資料,或者對已經過期的資料進行了修改。而主從複制,就是Redis對這多台伺服器進行資料同步的機制。

在主從複制機制中,Redis将伺服器分為主伺服器和從伺服器,主伺服器負責接收使用者送出的修改指令,修改資料庫中的資料,同時将修改同步到從伺服器中,而從伺服器的任務就是與主伺服器進行資料同步,并分擔本應該由主伺服器執行的查詢請求,減小主伺服器的壓力,除此之外,為了減輕主伺服器的壓力,我們也可以關閉主伺服器的持久化操作,而讓從伺服器來進行持久化。

2|22.2 主從複制的實作過程

完整的主從複制包含以下兩步:

同步:将從伺服器目前的狀态,更新為主伺服器目前的狀态,也就是使用主伺服器中存儲的資料,替換掉從伺服器的資料;

指令傳播:主伺服器執行每一次修改操作後,都需要告知從伺服器,讓從伺服器執行相同的操作,以保證一緻性;

下面我就來分别分析一下這兩個過程的詳細實作。

2|32.3 同步的實作原理

從伺服器與主伺服器同步,需要使用到SYNC指令,詳細的執行流程如下:

從伺服器連接配接到主伺服器,并向主伺服器發送SYNC指令;

主伺服器接收到SYNC指令後,開始執行BGSAVE指令(快照持久化),此時主伺服器将調用fock(),建立一個子程序,子程序去生成Redis目前狀态的一個快照;在這個過程中,新到達主伺服器的寫指令将會被記錄在緩沖區;

主伺服器執行完BGSAVE後,将快照檔案發送給從伺服器,在發送的過程中,如果還有新的寫指令到達,也會繼續記錄在緩沖區;從伺服器接收到主伺服器發來的快照檔案後,将丢棄自己記憶體中的資料,開始加載快照檔案中記錄的資料,加載完成後,就可以處理接收到的請求了;

主伺服器在發送完快照檔案後,開始将緩沖區中記錄的寫指令也同步到從伺服器;從伺服器接收到主伺服器發來的指令,便依次執行這些指令,執行完後,就與主伺服器的狀态一緻了;

2|42.4 指令傳播的實作原理

為什麼需要指令傳播?這個應該很好了解。經過上面的同步後,主伺服器與從伺服器存儲的資料就一緻了,這之後,從伺服器就可以分擔查詢操作,但是寫操作還是需要主伺服器完成。是以,雖然目前主從伺服器已經一緻,但是主伺服器如果執行了一次寫操作,而從伺服器沒有執行,它們又将變成不一緻的狀态。而指令傳播的實作原理很簡單:主伺服器每次執行寫操作,都會将這個寫指令發送給從伺服器,從伺服器接收到後,也執行這個寫指令,這樣就能讓主伺服器和從伺服器持續的保持一緻。

有人可能會想,為什麼是将指令發送到從伺服器,而不是重新執行一次同步操作呢?答案很簡單,因為上面的同步操作,需要很大的開銷。執行BGSAVE指令建立快照,需要建立一個子程序,同時生成一個檔案,需要進行大量的磁盤IO,在資料量很大的情況下,可能會使主伺服器産生數毫秒甚至是一秒的停頓。而向從伺服器傳輸一個指令的開銷,要比上面的同步小得多。

2|52.5 部分重同步介紹

以上介紹的主從複制過程,是一個開銷非常大,而且比較耗時的操作(主要是同步過程耗時),于是從Redis2.8開始,提供了一種優化機制——部分重同步。我們考慮這樣一種情況,假設一台從伺服器已經與主伺服器完成了同步,進入了指令傳播階段,但是由于某些原因,主從伺服器之間的網絡連接配接斷開了,從伺服器在一段時間後,重新連接配接上了主伺服器。按理來說,從伺服器和主伺服器斷開連接配接的這段時間,沒有同步對主伺服器的寫操作,此時它們已經不一緻了,那麼從伺服器需要重新執行一次主從複制,這又是一次非常耗時的操作。而Redis2.8之後,提供了一種優化機制,若在上面的情況發生時,如果滿足某些條件(具體條件之後叙述),可以不進行一次完整的主從複制,而是隻同步斷開連接配接的這段時間裡,沒有同步的操作,這就是部分重同步。

Redis2.8之後,提供了一個新的指令來實作部分重同步,這個指令就是PSYNC。從2.8開始,實作主從複制使用的就不是SYNC了,而是PSYNC,它可以算是SYNC的更新版本。PSYNC支援兩種模式:

完整重同步:如果Redis判斷目前從伺服器需要與主伺服器重新進行一次完整的主從複制,則PSYNC指令将執行與SYNC指令完全一樣的操作,上面已經描述過了,這裡就不重複叙述了;

部分重同步:若從伺服器與主伺服器斷線重連後,滿足某些條件,則不進行完整重同步,而是隻同步斷線過程中,沒有同步的部分;

2|62.6 部分重同步的實作原理

下面我們就來詳細分析一下,部分重同步是如何實作的。部分重同步需要依賴以下三個部分:

伺服器的運作id;

主伺服器的複制積壓緩沖區;

主從伺服器的複制偏移量;

(1)伺服器的運作 id

每一台伺服器都會被配置設定一個運作id,用來辨別伺服器的身份。從伺服器在與一台主伺服器連接配接後,會記錄主伺服器的id。從伺服器與主伺服器斷開後,可能會重新連接配接一台主伺服器,但是并不一定就是原來的那一台。當從伺服器連接配接到一台主伺服器後,會向主伺服器發送自己記錄的主伺服器id,主伺服器判斷這是不是自己,如果是,表明從伺服器之前連接配接的就是自己,則有可能可以使用部分重同步機制,否則,将重新進行一次完整同步。

(2)主伺服器的複制積壓緩沖區

首先,複制積壓緩沖區是一個固定長度,先進先出的隊列,預設 1MB。主伺服器在接收到使用者發來的寫指令時,不僅僅會将寫指令發送給從伺服器進行同步,同時還會将這個指令放入到複制積壓緩沖區中,目的是在從伺服器沒有成功接收到的時候能夠重傳。複制緩沖區的結構大緻如下:

可以看到,對于複制積壓緩沖區中的每一個位元組,都有一個對應的偏移量。如果目前緩沖區已經滿了,但是又有新的指令需要放入其中,則會将最先放入其中的指令移除,騰出足夠空間後,将新指令放入,也就是LRU算法(最近最久未使用),是以,緩沖區中能夠存儲的指令是有限的。

(3)主從伺服器的複制偏移量

主伺服器和從伺服器會分别維護自己的複制偏移量,主伺服器每發送出一個位元組,主伺服器偏移量就+1,而從伺服器每完成一個位元組的同步,從伺服器偏移量就+1。

什麼情況下會觸發部分重同步呢?答案就是:若從伺服器與主伺服器斷開連接配接,并重新連接配接到同一個主伺服器後,會将自己記錄的複制偏移量發送給主伺服器,主伺服器判斷這個偏移量之後的所有位元組,是否還在複制緩沖區中,如果在,則表明可以進行部分重同步,将複制緩沖區中,這個偏移量之後的所有位元組發送給從伺服器;若不完全包含,則表明從伺服器需要同步的資料,有一部分無法在緩沖區中找到,此時就需要進行一次完整同步。

2|72.7 配置從伺服器

下面講一講如何将一台Redis伺服器,配置為從伺服器,有兩種方式:

(1)配置檔案

可以在配置Redis的配置檔案中,加入以下配置項:

slaveof 主伺服器ip 主伺服器端口

在配置檔案中配置了上面這一行,則目前伺服器就是一台從伺服器,它啟動時,就會嘗試區連接配接上面上面這個配置項指定好的主伺服器,并在連接配接成功後發送PSYNC指令,完成之前介紹的步驟。

(2)指令

第二種方式就是使用指令,在Redis伺服器輸入下面這一行指令,目前伺服器就會作為一個從伺服器,嘗試連接配接主伺服器,并進行主從複制:

127.0.0.1:6379> SLAVEOF 主伺服器ip 主伺服器端口

2|82.8 主從複制的安全性

在使用Redis 複制功能時的設定中,強烈建議在 主伺服器 和 從伺服器 中啟用持久化。當不可能啟用時,例如由于非常慢的磁盤性能而導緻的延遲問題,應該配置執行個體來避免重置後自動重新開機。

為了更好地了解為什麼關閉了持久化并配置了自動重新開機的 主伺服器 是危險的,檢查以下故障模式,這些故障模式中資料會從 主伺服器 和所有 從伺服器 中被删除:

我們設定節點 A 為 主伺服器 并關閉它的持久化設定,節點 B 和 C 從 節點 A 複制資料。

節點 A 崩潰,但是他有一些自動重新開機的系統可以重新開機程序。但是由于持久化被關閉了,節點重新開機後其資料集合為空。

節點 B 和 節點 C會從節點 A 複制資料,但是節點 A 的資料集是空的,是以複制的結果是它們會銷毀自身之前的資料副本。

當 Redis Sentinel 被用于高可用并且 主伺服器 關閉持久化,這時如果允許自動重新開機程序也是很危險的。例如, 主伺服器 可以重新開機的足夠快以緻于 Sentinel 沒有探測到故障,是以上述的故障模式也會發生。任何時候資料安全性都是很重要的,是以如果 主伺服器 使用複制功能的同時未配置持久化,那麼自動重新開機程序這項應該被禁用。

3|0三、總結

以上就對Redis的主從複制做了一個比較詳細的描述,時間太晚了,就不說别的了,希望能夠為需要的人答疑解惑吧。

4|0四、參考

《Redis實戰》

Redis官方文檔—複制

https://www.jb51.net/article/156147.htm

EOF

本文作者:特務依昂

本文連結:

https://www.cnblogs.com/tuyang1129/p/12781631.html