天天看點

一文帶你深入了解 redis 複制技術及主從架構主從環境搭建複制技術的原理主從拓撲架構

主從架構可以說是網際網路必備的架構了,第一是為了保證服務的高可用,第二是為了實作讀寫分離,你可能熟悉我們常用的 MySQL 資料庫的主從架構,對于我們 redis 來說也不意外,redis 資料庫也有各種各樣的主從架構方式,在主從架構中會涉及到主節點與從節點之間的資料同步,這個資料同步的過程在 redis 中叫做複制,這在篇文章中,我們詳細的聊一聊 redis 的複制技術和主從架構 ,本文主要有以下内容:

  • 主從架構環境搭建
  • 主從架構的建立方式
  • 主從架構的斷開
  • 複制技術的原理
  • 資料同步過程
  • 心跳檢測
  • 主從拓撲架構
  • 一主一從
  • 一主多從
  • 樹狀結構

主從環境搭建

redis 的執行個體在預設的情況下都是主節點,是以我們需要修改一些配置來搭建主從架構,redis 的主從架構搭建還是比較簡單的,redis 提供了三種方式來搭建主從架構,在後面我們将就介紹,在介紹之前我們要先了解主從架構的特性:在主從架構中有一個主節點(master)和最少一個從節點(slave),并且資料複制是單向的,隻能從主節點複制到從節點,不能由從節點到主節點。

主從架構的建立有以下三種方式:

  • 在 Redis.conf 配置檔案中加入 slaveof {masterHost} {masterPort} 指令,随 Redis 執行個體的啟動生效
  • 在 redis-server 啟動指令後加入 --slaveof {masterHost} {masterPort} 參數
  • 在 redis-cli 互動視窗下直接使用指令:slaveof {masterHost} {masterPort}

上面三種方式都可以搭建 Redis 主從架構,我們以第一種方式來示範,其他兩種方式自行嘗試,由于是示範,是以就在本地啟動兩個 Redis 執行個體,并不在多台機器上啟動 redis 的執行個體了,我們準備一個端口 6379 的主節點執行個體,準備一個端口 6480 從節點的執行個體,端口 6480 的 redis 執行個體配置檔案取名為 

6480.conf

 并且在裡面添加 slaveof 語句,在配置檔案最後加入如下一條語句

slaveof 127.0.0.1 6379           

分别啟動兩個 redis 執行個體,啟動之後他們會自動建立主從關系,關于這背後的原理,我們後面在詳細的聊一聊,先來驗證一下我們的主從架構是否搭建成功,我們先在 6379 master 節點上新增一條資料:

一文帶你深入了解 redis 複制技術及主從架構主從環境搭建複制技術的原理主從拓撲架構

然後再 6480 slave 節點上擷取該資料:

一文帶你深入了解 redis 複制技術及主從架構主從環境搭建複制技術的原理主從拓撲架構

可以看出我們在 slave 節點上已經成功的擷取到了在 master 節點新增的值,說明主從架構已經搭建成功了,我們使用 info replication 指令來檢視兩個節點的資訊,先來看看主節點的資訊

一文帶你深入了解 redis 複制技術及主從架構主從環境搭建複制技術的原理主從拓撲架構

可以看出 6379 端口的執行個體 role 為 master,有一個正在連接配接的執行個體,還有其他運作的資訊,我們再來看看 6480 端口的 redis 執行個體資訊

一文帶你深入了解 redis 複制技術及主從架構主從環境搭建複制技術的原理主從拓撲架構

可以看出兩個節點之間互相記錄着對象的資訊,這些資訊在資料複制時候将會用到。在這裡有一點需要說明一下,預設情況下 slave 節點是隻讀的,并不支援寫入,也不建議開啟寫入,我們可以驗證一下,在 6480 執行個體上寫入一條資料

127.0.0.1:6480> set x 3           

提示隻讀,并不支援寫入操作,當然我們也可以修改該配置,在配置檔案中 

replica-read-only yes

 配置項就是用來控制從伺服器隻讀的,為什麼隻能隻讀?因為我們知道複制是單向的,資料隻能由 master 到 slave 節點,如果在 salve 節點上開啟寫入的話,那麼修改了 slave 節點的資料, master 節點是感覺不到的,slave 節點的資料并不能複制到 master 節點上,這樣就會造成資料不一緻的情況,是以建議 slave 節點隻讀。

主從架構的斷開同樣是 slaveof 指令,在從節點上執行 slaveof no one 指令就可以與主節點斷開追随關系,我們在 6480 節點上執行 slaveof no one 指令

127.0.0.1:6480> slaveof no one           

執行完 slaveof no one 指令之後,6480 節點的角色立馬恢複成了 master ,我們再來看看時候還和 6379 執行個體連接配接在一起,我們在 6379 節點上新增一個 key-value

127.0.0.1:6379> set y 3           

在 6480 節點上 get y

127.0.0.1:6480> get y           

在 6480 節點上擷取不到 y ,因為 6480 節點已經跟 6379 節點斷開的聯系,不存在主從關系了,slaveof 指令不僅能夠斷開連接配接,還能切換主伺服器,使用指令為 

slaveof {newMasterIp} {newMasterPort}

,我們讓 6379 成為 6480 的從節點, 在 6379 節點上執行 

slaveof 127.0.0.1 6480

 指令,我們在來看看 6379 的 info replication

127.0.0.1:6379> info replication           

6379 節點的角色已經是 slave 了,并且主節點的是 6480 ,我們可以再看看 6480 節點的 info replication

127.0.0.1:6480> info replication           

在 6480 節點上有 6379 從節點的資訊,可以看出 slaveof 指令已經幫我們完成了主伺服器的切換。

redis 的主從架構好像很簡單一樣,我們就執行了一條指令就成功搭建了主從架構,并且資料複制也沒有問題,使用起來确實簡單,但是這背後 redis 還是幫我們做了很多的事情,比如主從伺服器之間的資料同步、主從伺服器的狀态檢測等,這背後 redis 是如何實作的呢?接下來我們就一起看看

資料複制原理

我們執行完 slaveof 指令之後,我們的主從關系就建立好了,在這個過程中, master 伺服器與 slave 伺服器之間需要經曆多個步驟,如下圖所示:

一文帶你深入了解 redis 複制技術及主從架構主從環境搭建複制技術的原理主從拓撲架構

slaveof 指令背後,主從伺服器大緻經曆了七步,其中權限驗證這一步不是必須的,為了能夠更好的了解這些步驟,就以我們上面搭建的 redis 執行個體為例來詳細聊一聊各步驟。

1、儲存主節點資訊

在 6480 的用戶端向 6480 節點伺服器發送 

slaveof 127.0.0.1 6379

 指令時,我們會立馬得到一個 OK

127.0.0.1:6480> slaveof 127.0.0.1 6379           

這時候資料複制工作并沒有開始,資料複制工作是在傳回 OK 之後才開始執行的,這時候 6480 從節點做的事情是将給定的主伺服器 IP 位址 127.0.0.1 以及端口 6379 儲存到伺服器狀态的 masterhost 屬性和 masterport 屬性裡面

2、建立 socket 連接配接

在 slaveof 指令執行完之後,從伺服器會根據指令設定的 IP 位址和端口,跟主伺服器建立套接字連接配接, 如果從伺服器能夠跟主伺服器成功的建立 socket 連接配接,那麼從伺服器将會為這個 socket 關聯一個專門用于處理複制工作的檔案事件處理器,這個處理器将負責後續的複制工作,比如接受全量複制的 RDB 檔案以及伺服器傳來的寫指令。同樣主伺服器在接受從伺服器的 socket 連接配接之後,将為該 socket 建立一個用戶端狀态,這時候的從伺服器同時具有伺服器和用戶端兩個身份,從伺服器可以向主伺服器發送指令請求而主伺服器則會向從伺服器傳回指令回複。

3、發送 ping 指令

從伺服器與主伺服器連接配接成功後,做的第一件事情就是向主伺服器發送一個 ping 指令,發送 ping 指令主要有以下目的:

  • 檢測主從之間網絡套接字是否可用
  • 檢測主節點目前是否可接受處理指令

在發送 ping 指令之後,正常情況下主伺服器會傳回 pong 指令,接受到主伺服器傳回的 pong 回複之後就會進行下一步工作,如果沒有收到主節點的 pong 回複或者逾時,比如網絡逾時或者主節點正在阻塞無法響應指令,從伺服器會斷開複制連接配接,等待下一次定時任務的排程。

4、身份驗證

從伺服器在接收到主伺服器傳回的 pong 回複之後,下一步要做的事情就是根據配置資訊決定是否需要身份驗證:

  • 如果從伺服器設定了 masterauth 參數,則進行身份驗證
  • 如果從伺服器沒有設定 masterauth 參數,則不進行身份驗證

在需要身份驗證的情況下,從伺服器将就向主伺服器發送一條 auth 指令,指令參數為從伺服器 masterauth 選項的值,舉個例子,如果從伺服器的配置裡将 masterauth 參數設定為:123456,那麼從伺服器将向主伺服器發送 auth 123456 指令,身份驗證的過程也不是一帆風順的,可能會遇到以下幾種情況:

  • 從伺服器通過 auth 指令發送的密碼與主伺服器的 requirepass 參數值一緻,那麼将繼續進行後續操作,如果密碼不一緻,主服務将傳回一個 invalid password 錯誤
  • 如果主伺服器沒有設定 requirepass 參數,那麼主伺服器将傳回一個 no password is set 錯誤

所有的錯誤情況都會令從伺服器中止目前的複制工作,并且要從建立 socket 開始重新發起複制流程,直到身份驗證通過或者從伺服器放棄執行複制為止

5、發送端口資訊

在身份驗證通過後,從伺服器将執行 REPLCONF listening<port-number style="margin: 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important;">指令,向主伺服器發送從伺服器的監聽端口号,例如在我們的例子中從伺服器監聽的端口為 6480,那麼從伺服器将向主伺服器發送 REPLCONF listening 6480 指令,主伺服器接收到這個指令之後,會将端口号記錄在從伺服器所對應的用戶端狀态的 slave_listening_port 屬性了,也就是我們在 master 伺服器的 info replication 裡面看到的 port 值。</port-number>

6、資料複制

資料複制是最複雜的一塊了,由 psync 指令來完成,從伺服器會向主伺服器發送一個 psync 指令來進行資料同步,在 redis 2.8 版本以前使用的是 sync 指令,除了指令不同之外,在複制的方式上也有很大的不同,在 redis 2.8 版本以前使用的都是全量複制,這對主節點和網絡會造成很大的開銷,在 redis 2.8 版本以後,資料同步将分為全量同步和部分同步。

  • 全量複制:一般用于初次複制場景,不管是新舊版本的 redis 在從伺服器第一次與主服務連接配接時都将進行一次全量複制,它會把主節點的全部資料一次性發給從節點,當資料較大時,會對主節點和網絡造成很大的開銷,redis 的早期版本隻支援全量複制,這不是一種高效的資料複制方式
  • 部分複制:用于處理在主從複制中因網絡閃斷等原因造成的資料丢失 場景,當從節點再次連上主節點後,如果條件允許,主節點會補發丢失資料 給從節點。因為補發的資料遠遠小于全量資料,可以有效避免全量複制的過高開銷,部分複制是對老版複制的重大優化,有效避免了不必要的全量複制操作

redis 之是以能夠支援全量複制和部分複制,主要是對 sync 指令的優化,在 redis 2.8 版本以後使用的是一個全新的 psync 指令,指令格式為:psync {runId} {offset},這兩個參數的意義:

  • runId:主節點運作的id
  • offset:目前從節點複制的資料偏移量

也許你對上面的 runid、offset 比較陌生,沒關系,我們先來看看下面三個概念:

1、複制偏移量

參與複制的主從節點都會分别維護自身複制偏移量:主伺服器每次向從伺服器傳播 N 個位元組的資料時,就将自己的偏移量的值加上 N,從伺服器每次接收到主伺服器傳播的 N個位元組的資料時,将自己的偏移量值加上 N。通過對比主從伺服器的複制偏移量,就可以知道主從伺服器的資料是否一緻,如果主從伺服器的偏移量總是相同,那麼主從資料一緻,相反,如果主從伺服器兩個的偏移量并不相同,那麼說明主從伺服器并未處于資料一緻的狀态,比如在有多個從伺服器時,在傳輸的過程中某一個伺服器離線了,如下圖所示:

一文帶你深入了解 redis 複制技術及主從架構主從環境搭建複制技術的原理主從拓撲架構

由于從伺服器A 在資料傳輸時,由于網絡原因掉線了,導緻偏移量與主伺服器不一緻,那麼當從伺服器A 重新開機并且與主伺服器連接配接成功後,重新向主伺服器發送 psync 指令,這時候資料複制應該執行全量複制還是部分複制呢?如果執行部分複制,主伺服器又如何補償從伺服器A 在斷線期間丢失的那部分資料呢?這些問題的答案都在複制積壓緩沖區裡面

2、複制積壓緩沖區

複制積壓緩沖區是儲存在主節點上的一個固定長度的隊列,預設大小為 1MB,當主節點有連接配接的從節點(slave)時被建立,這時主節點(master) 響應寫指令時,不但會把指令發送給從節點,還會寫入複制積壓緩沖區,如下圖所示:

一文帶你深入了解 redis 複制技術及主從架構主從環境搭建複制技術的原理主從拓撲架構

是以,主伺服器的複制積壓緩沖區裡面會儲存着一部分最近傳播的寫指令,并且複制積壓緩沖區會為隊列中的每個位元組記錄相應的複制偏移量。是以當從伺服器重新連上主伺服器時,從伺服器通過 psync 指令将自己的複制偏移量 offset 發送給主伺服器,主伺服器會根據這個複制偏移量來決定對從伺服器執行何種資料同步操作:

  • 如果從伺服器的複制偏移量之後的資料仍然存在于複制積壓緩沖區裡面,那麼主伺服器将對從伺服器執行部分複制操作
  • 如果從伺服器的複制偏移量之後的資料不存在于複制積壓緩沖區裡面,那麼主伺服器将對從伺服器執行全量複制操作

3、伺服器運作ID

每個 Redis 節點啟動後都會動态配置設定一個 40 位的十六進制字元串作為運作 ID,運作 ID 的主要作用是用來唯一識别 Redis 節點,我們可以使用 

info server

 指令來檢視

127.0.0.1:6379> info server           

這裡面有一個

run_id

 字段就是伺服器運作的ID

了解這幾個概念之後,我們一起來看看 psync 指令的運作流程,psync 指令運作流程如下圖所示:

一文帶你深入了解 redis 複制技術及主從架構主從環境搭建複制技術的原理主從拓撲架構

psync 指令的邏輯比較簡單,整個流程分為兩步:

1、從節點發送 psync 指令給主節點,參數 runId 是目前從節點儲存的主節點運作ID,參數offset是目前從節點儲存的複制偏移量,如果是第一次參與複制則預設值為 -1。

2、主節點接收到 psync 指令之後,會向從伺服器傳回以下三種回複中的一種:

  • 回複 +FULLRESYNC {runId} {offset}:表示主伺服器将與從伺服器執行一次全量複制操作,其中 runid 是這個主伺服器的運作 id,從伺服器會儲存這個id,在下一次發送 psync 指令時使用,而 offset 則是主伺服器目前的複制偏移量,從伺服器會将這個值作為自己的初始化偏移量
  • 回複 +CONTINUE:那麼表示主伺服器與從伺服器将執行部分複制操作,從伺服器隻要等着主伺服器将自己缺少的那部分資料發送過來就可以了
  • 回複 +ERR:那麼表示主伺服器的版本低于 redis 2.8,它識别不了 psync 指令,從伺服器将向主伺服器發送 sync 指令,并與主伺服器執行全量複制

7、指令持續複制

當主節點把目前的資料同步給從節點後,便完成了複制的建立流程。但是主從伺服器并不會斷開連接配接,因為接下來主節點會持續地把寫指令發送給從節點,保證主從資料一緻性。

經過上面 7 步就完成了主從伺服器之間的資料同步,由于這篇文章的篇幅比較長,關于全量複制和部分複制的細節就不介紹了,全量複制就是将主節點的目前的資料生産 RDB 檔案,發送給從伺服器,從伺服器再從本地磁盤加載,這樣當檔案過大時就需要特别大的網絡開銷,不然由于資料傳輸比較慢會導緻主從資料延時較大,部分複制就是主伺服器将複制積壓緩沖區的寫指令直接發送給從伺服器。

心跳檢測是發生在主從節點在建立複制後,它們之間維護着長連接配接并彼此發送心跳指令,便以後續持續發送寫指令,主從心跳檢測如下圖所示:

一文帶你深入了解 redis 複制技術及主從架構主從環境搭建複制技術的原理主從拓撲架構

主從節點彼此都有心跳檢測機制,各自模拟成對方的用戶端進行通信,主從心跳檢測的規則如下:

  • 主節點預設每隔 10 秒對從節點發送 ping 指令,判斷從節點的存活性和連接配接狀态。可通過修改 redis.conf 配置檔案裡面的 repl-ping-replica-period 參數來控制發送頻率
  • 從節點在主線程中每隔 1 秒發送 replconf ack {offset} 指令,給主節點 上報自身目前的複制偏移量,這條指令除了檢測主從節點網絡之外,還通過發送複制偏移量來保證主從的資料一緻

主節點根據 replconf 指令判斷從節點逾時時間,展現在 info replication 統 計中的 lag 資訊中,我們在主伺服器上執行 info replication 指令:

127.0.0.1:6379> info replication           

可以看出 slave0 字段的值最後面有一個 lag,lag 表示與從節點最後一次通信延遲的秒數,正常延遲應該在 0 和 1 之間。如果超過 repl-timeout 配置的值(預設60秒),則判定從節點下線并斷開複制用戶端連接配接,如果從節點重新恢複,心跳檢測會繼續進行。

Redis 的主從拓撲結構可以支援單層或多層複制關系,根據拓撲複雜性可以分為以下三種:一主一從、一主多從、樹狀主從架構

一主一從結構

一主一從結構是最簡單的複制拓撲結構,我們前面搭建的就是一主一從的架構,架構如圖所示:

一文帶你深入了解 redis 複制技術及主從架構主從環境搭建複制技術的原理主從拓撲架構

一主一從架構用于主節點出現當機時從節點 提供故障轉移支援,當應用寫指令并發量較高且需要持久化時,可以隻在從節點上開啟 AOF,這樣既保證資料安全性同時也避免了持久化對主節點的性能幹擾。但是這裡有一個坑,需要你注意,就是當主節點關閉持久化功能時, 如果主節點脫機要避免自動重新開機操作。因為主節點之前沒有開啟持久化功能自動重新開機後資料集為空,這時從節點如果繼續複制主節點會導緻從節點資料也被清空的情況,喪失了持久化的意義。安全的做法是在從節點上執行 slaveof no one 斷開與主節點的複制關系,再重新開機主節點進而避免這一問題

一主多從架構

一主多從架構又稱為星形拓撲結構,一主多從架構如下圖所示:

一文帶你深入了解 redis 複制技術及主從架構主從環境搭建複制技術的原理主從拓撲架構

一主多從架構可以實作讀寫分離來減輕主伺服器的壓力,對于讀占比較大的場景,可以把讀指令發送到 從節點來分擔主節點壓力。同時在日常開發中如果需要執行一些比較耗時的讀指令,如:keys、sort等,可以在其中一台從節點上執行,防止慢查詢對主節點造成阻塞進而影響線上服務的穩定性。對于寫并發量較高的場景,多個從節點會導緻主節點寫指令的多次發送進而過度消耗網絡帶寬,同時也加重了主節點的負載影響服務穩定性。

樹狀主從架構