一、前言
最近由於疫情影響,時間比較多,所以開始學習之前一直想學,但是卻沒時間學的Redis
。這兩天研究了一下Redis
的持久化以及主從複製機制,現在已經很晚了,就不多廢話了。這篇部落格就來談一談Redis
的主從複製機制。在這裡需要提醒一下,主從複製依賴於Redis
的快照持久化(RDB
),所以如果不瞭解持久化,請先去研究那一塊的內容,可以看看這篇部落格:詳細分析Redis的持久化操作—RDB與AOF。
二、正文
2.1 什麼是主從複製
首先我們來談一談最基本的問題——什麼是主從複製,為什麼需要它?我們知道,現在的應用基本上都會使用叢集進行部署,同一個應用部署在多臺伺服器上,各臺伺服器互相同步,各自承擔一部分任務,以此來減輕單臺伺服器的壓力。而主從複製就是Redis
用來對儲存相同資料的多臺伺服器進行同步的機制。
假設我們只在一臺伺服器上部署了Redis
伺服器,那所有需要訪問Redis
伺服器的請求,都需要這一臺伺服器來處理,這對伺服器來說有很大的壓力。如果訪問的很頻繁,那麼一臺伺服器根本處理不過來,所以我們需要多臺伺服器同時部署Redis
,然後每一臺伺服器承擔一部分任務。
如果我們部署了多臺Redis
伺服器,儲存相同的資料,為同一個應用進行服務,那麼不難想到,我們需要處理一個問題——資料同步。我們必須保證這多臺伺服器的Redis
資料庫中,儲存的資料是一致的,而且都應該是正確的資料,否則將會導致對請求進行錯誤的處理,比如查詢出的是過期的資料,或者對已經過期的資料進行了修改。而主從複製,就是Redis
對這多臺伺服器進行資料同步的機制。
在主從複製機制中,Redis
將伺服器分為主伺服器和從伺服器,主伺服器負責接收使用者提交的修改指令,修改資料庫中的資料,同時將修改同步到從伺服器中,而從伺服器的任務就是與主伺服器進行資料同步,並分擔本應該由主伺服器執行的查詢請求,減小主伺服器的壓力,除此之外,為了減輕主伺服器的壓力,我們也可以關閉主伺服器的持久化操作,而讓從伺服器來進行持久化。
2.2 主從複製的實現過程
完整的主從複製包含以下兩步:
- 同步:將從伺服器當前的狀態,更新為主伺服器當前的狀態,也就是使用主伺服器中儲存的資料,替換掉從伺服器的資料;
- 命令傳播:主伺服器執行每一次修改操作後,都需要告知從伺服器,讓從伺服器執行相同的操作,以保證一致性;
下面我就來分別分析一下這兩個過程的詳細實現。
2.3 同步的實現原理
從伺服器與主伺服器同步,需要使用到SYNC
指令,詳細的執行流程如下:
- 從伺服器連線到主伺服器,並向主伺服器傳送
SYNC
指令; - 主伺服器接收到
SYNC
指令後,開始執行BGSAVE
指令(快照持久化),此時主伺服器將呼叫fock()
,建立一個子程式,子程式去生成Redis
當前狀態的一個快照;在這個過程中,新到達主伺服器的寫指令將會被記錄在緩衝區; - 主伺服器執行完
BGSAVE
後,將快照檔案傳送給從伺服器,在傳送的過程中,如果還有新的寫指令到達,也會繼續記錄在緩衝區;從伺服器接收到主伺服器發來的快照檔案後,將丟棄自己記憶體中的資料,開始載入快照檔案中記錄的資料,載入完成後,就可以處理接收到的請求了; - 主伺服器在傳送完快照檔案後,開始將緩衝區中記錄的寫指令也同步到從伺服器;從伺服器接收到主伺服器發來的指令,便依次執行這些指令,執行完後,就與主伺服器的狀態一致了;
2.4 命令傳播的實現原理
為什麼需要命令傳播?這個應該很好理解。經過上面的同步後,主伺服器與從伺服器儲存的資料就一致了,這之後,從伺服器就可以分擔查詢操作,但是寫操作還是需要主伺服器完成。所以,雖然當前主從伺服器已經一致,但是主伺服器如果執行了一次寫操作,而從伺服器沒有執行,它們又將變成不一致的狀態。而命令傳播的實現原理很簡單:主伺服器每次執行寫操作,都會將這個寫指令傳送給從伺服器,從伺服器接收到後,也執行這個寫指令,這樣就能讓主伺服器和從伺服器持續的保持一致。
有人可能會想,為什麼是將指令傳送到從伺服器,而不是重新執行一次同步操作呢?答案很簡單,因為上面的同步操作,需要很大的開銷。執行BGSAVE
指令建立快照,需要建立一個子程式,同時生成一個檔案,需要進行大量的磁碟IO,在資料量很大的情況下,可能會使主伺服器產生數毫秒甚至是一秒的停頓。而向從伺服器傳輸一個指令的開銷,要比上面的同步小得多。
2.5 部分重同步介紹
以上介紹的主從複製過程,是一個開銷非常大,而且比較耗時的操作(主要是同步過程耗時),於是從Redis2.8
開始,提供了一種優化機制——部分重同步。我們考慮這樣一種情況,假設一臺從伺服器已經與主伺服器完成了同步,進入了命令傳播階段,但是由於某些原因,主從伺服器之間的網路連線斷開了,從伺服器在一段時間後,重新連線上了主伺服器。按理來說,從伺服器和主伺服器斷開連線的這段時間,沒有同步對主伺服器的寫操作,此時它們已經不一致了,那麼從伺服器需要重新執行一次主從複製,這又是一次非常耗時的操作。而Redis2.8
之後,提供了一種優化機制,若在上面的情況發生時,如果滿足某些條件(具體條件之後敘述),可以不進行一次完整的主從複製,而是隻同步斷開連線的這段時間裡,沒有同步的操作,這就是部分重同步。
Redis2.8
之後,提供了一個新的指令來實現部分重同步,這個指令就是PSYNC。從2.8
開始,實現主從複製使用的就不是SYNC
了,而是PSYNC
,它可以算是SYNC
的升級版本。PSYNC
支援兩種模式:
- 完整重同步:如果
Redis
判斷當前從伺服器需要與主伺服器重新進行一次完整的主從複製,則PSYNC
指令將執行與SYNC
指令完全一樣的操作,上面已經描述過了,這裡就不重複敘述了; - 部分重同步:若從伺服器與主伺服器斷線重連後,滿足某些條件,則不進行完整重同步,而是隻同步斷線過程中,沒有同步的部分;
2.6 部分重同步的實現原理
下面我們就來詳細分析一下,部分重同步是如何實現的。部分重同步需要依賴以下三個部分:
- 伺服器的執行
id
; - 主伺服器的複製積壓緩衝區;
- 主從伺服器的複製偏移量;
(1)伺服器的執行 id
每一臺伺服器都會被分配一個執行id
,用來標識伺服器的身份。從伺服器在與一臺主伺服器連線後,會記錄主伺服器的id
。從伺服器與主伺服器斷開後,可能會重新連線一臺主伺服器,但是並不一定就是原來的那一臺。當從伺服器連線到一臺主伺服器後,會向主伺服器傳送自己記錄的主伺服器id
,主伺服器判斷這是不是自己,如果是,表明從伺服器之前連線的就是自己,則有可能可以使用部分重同步機制,否則,將重新進行一次完整同步。
(2)主伺服器的複製積壓緩衝區
首先,複製積壓緩衝區是一個固定長度,先進先出的佇列,預設 1MB
。主伺服器在接收到使用者發來的寫指令時,不僅僅會將寫指令傳送給從伺服器進行同步,同時還會將這個指令放入到複製積壓緩衝區中,目的是在從伺服器沒有成功接收到的時候能夠重傳。複製緩衝區的結構大致如下:
可以看到,對於複製積壓緩衝區中的每一個位元組,都有一個對應的偏移量。如果當前緩衝區已經滿了,但是又有新的指令需要放入其中,則會將最先放入其中的指令移除,騰出足夠空間後,將新指令放入,也就是LRU
演算法(最近最久未使用),所以,緩衝區中能夠儲存的指令是有限的。
(3)主從伺服器的複製偏移量
主伺服器和從伺服器會分別維護自己的複製偏移量,主伺服器每傳送出一個位元組,主伺服器偏移量就+1
,而從伺服器每完成一個位元組的同步,從伺服器偏移量就+1
。
什麼情況下會觸發部分重同步呢?答案就是:若從伺服器與主伺服器斷開連線,並重新連線到同一個主伺服器後,會將自己記錄的複製偏移量傳送給主伺服器,主伺服器判斷這個偏移量之後的所有位元組,是否還在複製緩衝區中,如果在,則表明可以進行部分重同步,將複製緩衝區中,這個偏移量之後的所有位元組傳送給從伺服器;若不完全包含,則表明從伺服器需要同步的資料,有一部分無法在緩衝區中找到,此時就需要進行一次完整同步。
2.7 配置從伺服器
下面講一講如何將一臺Redis
伺服器,配置為從伺服器,有兩種方式:
(1)配置檔案
可以在配置Redis
的配置檔案中,加入以下配置項:
slaveof 主伺服器ip 主伺服器埠
在配置檔案中配置了上面這一行,則當前伺服器就是一臺從伺服器,它啟動時,就會嘗試區連線上面上面這個配置項指定好的主伺服器,並在連線成功後傳送PSYNC
指令,完成之前介紹的步驟。
(2)指令
第二種方式就是使用指令,在Redis
伺服器輸入下面這一行指令,當前伺服器就會作為一個從伺服器,嘗試連線主伺服器,並進行主從複製:
127.0.0.1:6379> SLAVEOF 主伺服器ip 主伺服器埠
2.8 主從複製的安全性
在使用Redis
複製功能時的設定中,強烈建議在 主伺服器 和 從伺服器 中啟用持久化。當不可能啟用時,例如由於非常慢的磁碟效能而導致的延遲問題,應該配置例項來避免重置後自動重啟。
為了更好地理解為什麼關閉了持久化並配置了自動重啟的 主伺服器 是危險的,檢查以下故障模式,這些故障模式中資料會從 主伺服器 和所有 從伺服器 中被刪除:
- 我們設定節點
A
為 主伺服器 並關閉它的持久化設定,節點B
和C
從 節點A
複製資料。 - 節點
A
崩潰,但是他有一些自動重啟的系統可以重啟程式。但是由於持久化被關閉了,節點重啟後其資料集合為空。 - 節點
B
和 節點C
會從節點A
複製資料,但是節點A
的資料集是空的,因此複製的結果是它們會銷燬自身之前的資料副本。
當 Redis Sentinel
被用於高可用並且 主伺服器 關閉持久化,這時如果允許自動重啟程式也是很危險的。例如, 主伺服器 可以重啟的足夠快以致於 Sentinel
沒有探測到故障,因此上述的故障模式也會發生。任何時候資料安全性都是很重要的,所以如果 主伺服器 使用複製功能的同時未配置持久化,那麼自動重啟程式這項應該被禁用。
三、總結
以上就對Redis
的主從複製做了一個比較詳細的描述,時間太晚了,就不說別的了,希望能夠為需要的人答疑解惑吧。