天天看點

一篇搞定Redis主從複制原理和實作方式

作者:添甄
滿懷憂思,不如先幹再說!

主從複制介紹

主從複制是将一台Redis伺服器的資料,複制到其他Redis伺服器上,前者稱為主節點(master/leader),後者稱為從節點(slave/follower),一個主節點可以有多個從節點,但是一個從節點隻能有一個主節點,資料的複制是單向的,隻能從主節點複制到從節點。預設情況下,每一個Redis伺服器都是一個主節點。

一篇搞定Redis主從複制原理和實作方式

作用

  1. 資料備份:主從複制實作了資料的熱備份,提高資料的安全性
  2. 故障恢複:當主節點發生問題不能提供服務時,從節點可以提供服務,保證Redis叢集的可用性
  3. 負載均衡:在主從複制的基礎上,可以實作讀寫分離,主節點負責寫操作,從節點負責讀操作,在寫少讀多的場景下,可以使用該模式大大提高Redis的并發量
  4. Redis高可用:主從複制是實作哨兵和叢集的基礎,主從複制是Redis高可用的基石

主從複制實作

叢集準備

主從複制不會在同一台節點上,我們準備三台虛拟機,分别為test201,test202,test203,Redis端口分别為6379,6380和6381,我們将test201:6379當做主節點,餘下兩個為從節點。我們使用之前講解的配置檔案方式啟動三台Redis節點。

實作方式

1、用戶端指令:Redis服務啟動後在用戶端使用slaveof <masterip> <masterport>,該Redis服務會成為從節點。

127.0.0.1:6380> slaveof test201 6379           

2、啟動指令:使用redis-server指令時使用 --slaveof <masterip> <masterport>,該Redis會成為從節點

redis-server redis-6381.conf --slaveof test201 6379           

3、配置檔案:在從節點的配置檔案中配置 slaveof <masterip> <masterport>

slaveof test201 6379            
敲黑闆:此三種方式實作Redis的主從複制,實作資料的熱備份,即主節點新增,删除,修改key,從節點也會實時更新。

斷開主從關系

哪天想單飛或者另尋明主需要斷開現在主從關系,在從節點上運作slaveof no one,實作斷開,那麼從節點上的資料不會清空,但是不再接收主節點的資料更新

檢視主從複制關系

在用戶端執行以下指令

info replication           

test201:主節點,可以看出該節點是master,主節點,有一個從節點,ip為192.168.109.203,端口為6381

一篇搞定Redis主從複制原理和實作方式

test203:從節點,可以看出該節點是slave,主節點是test201,端口為6379

一篇搞定Redis主從複制原理和實作方式

資料同步概述

在Redis複制的基礎上從節點(slave)可以精确的複制主節點(master)上的資料,每當slave和master之間斷開連接配接時,slave會自動連接配接上master,并且無論這期間master發生了什麼,slave都會嘗試去同步精确的資料,稱為master的精确副本。這個過程就是資料同步

Redis2.8之前,從節點向主節點發送sync指令請求資料同步,此時的同步時全量複制,Redis2.8之後從節點發送psync指令進行資料同步,此時根據主從節點目前狀态不同,同步的結果可能是全量複制或者部分複制。

  1. 全量複制:用于初次複制或其他無法部分複制的情況,将主節點中的所有資料都發送給從節點,是一個很重的操作。
  2. 部分複制:用于網絡中斷等情況的複制,隻将中斷期間主節點的寫操作發送給從節點,相比全量複制不比較高效,如果連接配接斷開時間過長,導緻主節點沒有完整的儲存斷開時間内的資料,那麼還是會使用全量複制。

同步機制

  • 當一個 master 執行個體和一個 slave 執行個體連接配接正常時, master 會發送一連串的指令流來保持對 slave 的更新,以便于将自身資料集的改變複制給 slave , :包括用戶端的寫入、key 的過期或被逐出等等。
  • 當 master 和 slave 之間的連接配接斷開之後,因為網絡問題、或者是主從意識到連接配接逾時, slave 重新連接配接上 master 并會嘗試進行部分重同步:這意味着它會嘗試隻擷取在斷開連接配接期間内丢失的指令流。
  • 當無法進行部分重同步時, slave 會請求進行全量重同步。這會涉及到一個更複雜的過程,例如 master 需要建立所有資料的快照,将之發送給 slave ,之後在資料集更改時持續發送指令流到 slave 。

Redis使用預設的異步複制,其特點是低延遲和高性能,是絕大多數 Redis 用例的自然複制模式。但是,slave會異步地确認其從master周期接收到的資料量。

全量複制

  1. slave判斷無法進行部分複制,向master發送全量複制請求,或slave發送部分複制,master判斷無法進行全量複制;
  2. master接收到全量複制指令之後,運作bgsave,在背景生成RDB檔案,并生成一個緩沖區(複制緩沖區),記錄從現在開始執行的所有寫指令;
  3. master的bgsave執行完畢之後,将RDB檔案發送給slave,slave首先清除自身舊資料,然後載入接收的RDB檔案,将資料庫狀态更新至master執行bgsave時的資料庫狀态;
  4. master将上文提到的複制緩沖區中所有寫指令發送給slave,slave執行這些寫指令,将資料庫狀态更新至master最新狀态;
  5. 如果slave開啟了AOF,則會觸發bgrewriteaof操作,進而保證AOF檔案更新到最新狀态。

從上述可以看出全量複制是一個很重的操作:

  1. master通過bgsave使進行fork子程序進行RDB持久化,該過程非常消耗CPU、記憶體、硬碟IO;
  2. master通過網絡将RDB檔案發送給slave,對主/從節點的帶寬都有很大損耗;
  3. slvae清空老資料,載入新的RDB檔案,這個過程是阻塞的,無法響應用戶端指令,slave執行bgrewrieaof也會到來損耗。

部分複制

因為全量複制損耗太大,Redis2.8版本開始提供部分複制,用于處理網絡中斷時的資料同步。部分複制實作主要依賴于三個重要的概念:複制偏移量,複制積壓緩沖區,伺服器運作ID。

複制偏移量

主節點和從節點分别維護一個複制偏移量(offset),代表的是master向slave傳遞的位元組數;主節點每次向從節點傳播N個位元組資料時,主節點的offset增加N;從節點每次收到主節點傳來的N個位元組資料時,從節點的offset增加N。offset用于判斷主從節點的資料庫狀态是否一緻:如果二者offset相同,則一緻;如果offset不同,則不一緻,此時可以根據兩個offset找出從節點缺少的那部分資料。例如,如果主節點的offset是1000,而從節點的offset是500,那麼部分複制就需要将offset為501-1000的資料傳遞給從節點。而offset為501-1000的資料存儲的位置,就是下面要介紹的複制積壓緩沖區。

一篇搞定Redis主從複制原理和實作方式

複制積壓緩沖區

當主節點開始有從節點時建立,其作用是備份主節點最近發送給從節點的資料,複制積壓緩沖區由主節點建立、維護、固定長度、先進先出。無論主節點有幾個從節點,都隻需要一個複制積壓緩沖區,預設大小為1M

在指令傳播階段,主節點除了将寫指令發送給從節點外還會發送一份給複制積壓緩沖區,作為寫指令的備份,除了存儲寫指令,複制積壓緩沖區中還存儲了其中的每個位元組對應的複制偏移量(offset)。由于複制積壓緩沖區定長且是先進先出,是以它儲存的是主節點最近執行的寫指令;時間較早的寫指令會被擠出緩沖區。

由于該緩沖區長度固定且有限,是以可以備份的寫指令也有限,當主從節點offset的差距過大超過緩沖區長度時,将無法執行部分複制,隻能執行全量複制。反過來說,為了提高網絡中斷時部分複制執行的機率,可以根據需要增大複制積壓緩沖區的大小(通過配置repl-backlog-size);例如如果網絡中斷的平均時間是60s,而主節點平均每秒産生的寫指令(特定協定格式)所占的位元組數為100KB,則複制積壓緩沖區的平均需求為6MB,保險起見,可以設定為12MB,來保證絕大多數斷線情況都可以使用部分複制

從節點将offset發送給主節點後,主節點根據offset和緩沖區大小決定能否執行部分複制:

  • 如果offset偏移量之後的資料,仍然都在複制積壓緩沖區裡,則執行部分複制;
  • 如果offset偏移量之後的資料已不在複制積壓緩沖區中(資料已被擠出),則執行全量複制。

伺服器運作ID(runid)

每個Redis節點(無論主從),在啟動時都會自動生成一個随機ID(每次啟動都不一樣),由40個随機的十六進制字元組成;

runid用來唯一識别一個Redis節點。通過info Server指令,可以檢視節點的runid。

主從節點初次複制時,主節點将自己的runid發送給從節點,從節點将這個runid儲存起來;當斷線重連時,從節點會将這個runid發送給主節點;主節點根據runid判斷能否進行部分複制:

  • 如果從節點儲存的runid與主節點現在的runid相同,說明主從節點之前同步過,主節點會繼續嘗試使用部分複制(到底能不能部分複制還要看offset和複制積壓緩沖區的情況);
  • 如果從節點儲存的runid與主節點現在的runid不同,說明從節點在斷線前同步的Redis節點并不是目前的主節點,隻能進行全量複制
一篇搞定Redis主從複制原理和實作方式

psync指令執行流程

一篇搞定Redis主從複制原理和實作方式

1、首先從節點根據目前狀态,決定如何調用psync指令:

  • 如果從節點之前未執行過slaveof或最近執行了slaveof no one,則從節點發送指令為psync ? -1,向主節點請求全量複制;
  • 如果從節點之前執行了slaveof,則發送指令為psync <runid> <offset>,其中runid為上次複制的主節點的runid,offset為上次複制截止時從節點儲存的複制偏移量。

2、主節點根據收到的psync指令,及目前伺服器狀态,決定執行全量複制還是部分複制:

  • 如果主節點版本低于Redis2.8,則傳回-ERR回複,此時從節點重新發送sync指令執行全量複制;
  • 如果主節點版本夠新,且runid與從節點發送的runid相同,且從節點發送的offset之後的資料在複制積壓緩沖區中都存在,則回複+CONTINUE,表示将進行部分複制,從節點等待主節點發送其缺少的資料即可;
  • 如果主節點版本夠新,但是runid與從節點發送的runid不同,或從節點發送的offset之後的資料已不在複制積壓緩沖區中(在隊列中被擠出了),則回複+FULLRESYNC <runid> <offset>,表示要進行全量複制,其中runid表示主節點目前的runid,offset表示主節點目前的offset,從節點儲存這兩個值,以備使用。

指令傳播階段

在指令傳播階段,除了發送寫指令,主從節點還維持着心跳機制:PING和REPLCONF ACK。心跳機制對于主從複制的逾時判斷、資料安全等有作用。

主->從:PING

每隔指定的時間,主節點會向從節點發送PING指令,這個PING指令的作用,主要是為了讓從節點進行逾時判斷。

PING發送的頻率由 repl-ping-slave-period 參數控制,機關是秒,預設值是10s。

從->主:REPLCONF ACK

在指令傳播階段,從節點會向主節點發送REPLCONF ACK指令,頻率是每秒1次;指令格式為:REPLCONF ACK {offset},其中offset指從節點儲存的複制偏移量。

REPLCONF ACK指令的作用包括:

(1)實時監測主從節點網絡狀态:該指令會被主節點用于複制逾時的判斷。此外,在主節點中使用info Replication,可以看到其從節點的狀态中的lag值,代表的是主節點上次收到該REPLCONF ACK指令的時間間隔,在正常情況下,該值應該是0或1。

(2)檢測指令丢失:從節點發送了自身的offset,主節點會與自己的offset對比,如果從節點資料缺失(如網絡丢包),主節點會推送缺失的資料(這裡也會利用複制積壓緩沖區)。注意,offset和複制積壓緩沖區,不僅可以用于部分複制,也可以用于處理指令丢失等情形;差別在于前者是在斷線重連後進行的,而後者是在主從節點沒有斷線的情況下進行的。

(3)輔助保證從節點的數量和延遲:Redis主節點中使用min-slaves-to-write和min-slaves-max-lag參數,來保證主節點在不安全的情況下不會執行寫指令;所謂不安全,是指從節點數量太少,或延遲過高。例如min-slaves-to-write和min-slaves-max-lag分别是3和10,含義是如果從節點數量小于3個,或所有從節點的延遲值都大于10s,則主節點拒絕執行寫指令。而這裡從節點延遲值的擷取,就是通過主節點接收到REPLCONF ACK指令的時間來判斷的,即前面所說的info Replication中的lag值。

總結

1、主從複制的作用:了解主從複制是為了解決什麼樣的問題,即資料備援、故障恢複、讀負載均衡等。

2、主從複制的操作:即slaveof指令。

3、主從複制的原理:主從複制包括了連接配接建立階段、資料同步階段、指令傳播階段;其中資料同步階段,有全量複制和部分複制兩種資料同步方式;指令傳播階段,主從節點之間有PING和REPLCONF ACK指令互相進行心跳檢測。

主從複制雖然可以對資料進行熱備份,但其缺陷仍很明顯:故障恢複無法自動化;寫操作無法負載均衡;存儲能力受到單機的限制;這些問題的解決,需要哨兵和叢集的幫助,但是主從複制是實作Redis高可用的基石非常重要,如哨兵模式和叢集都需要依賴主從實作。

你的支援是對我最大的肯定,如果不錯記得關注,點贊哦!

繼續閱讀