一文帶你深刻了解 redis 複製技術及主從架構

主從架構能夠說是互聯網必備的架構了,第一是爲了保證服務的高可用,第二是爲了實現讀寫分離,你可能熟悉咱們經常使用的 MySQL 數據庫的主從架構,對於咱們 redis 來講也不意外,redis 數據庫也有各類各樣的主從架構方式,在主從架構中會涉及到主節點與從節點之間的數據同步,這個數據同步的過程在 redis 中叫作複製,這在篇文章中,咱們詳細的聊一聊 redis 的複製技術和主從架構 ,本文主要有如下內容:java

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

主從環境搭建

redis 的實例在默認的狀況下都是主節點,因此咱們須要修改一些配置來搭建主從架構,redis 的主從架構搭建仍是比較簡單的,redis 提供了三種方式來搭建主從架構,在後面咱們將就介紹,在介紹以前咱們要先了解主從架構的特性:在主從架構中有一個主節點(master)和最少一個從節點(slave),而且數據複製是單向的,只能從主節點複製到從節點,不能由從節點到主節點。git

主從架構的創建方式

主從架構的創建有如下三種方式:redis

  • 在 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 節點上新增一條數據:api

master 節點新增數據

而後再 6480 slave 節點上獲取該數據:安全

slave 節點獲取數據

能夠看出咱們在 slave 節點上已經成功的獲取到了在 master 節點新增的值,說明主從架構已經搭建成功了,咱們使用 info replication 命令來查看兩個節點的信息,先來看看主節點的信息服務器

master info replication

能夠看出 6379 端口的實例 role 爲 master,有一個正在鏈接的實例,還有其餘運行的信息,咱們再來看看 6480 端口的 redis 實例信息微信

slave info replication

能夠看出兩個節點之間相互記錄着對象的信息,這些信息在數據複製時候將會用到。在這裏有一點須要說明一下,默認狀況下 slave 節點是隻讀的,並不支持寫入,也不建議開啓寫入,咱們能夠驗證一下,在 6480 實例上寫入一條數據網絡

127.0.0.1:6480> set x 3
(error) READONLY You can't write against a read only replica.
127.0.0.1:6480> 
複製代碼

提示只讀,並不支持寫入操做,固然咱們也能夠修改該配置,在配置文件中 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
OK
127.0.0.1:6480> info replication
# Replication
role:master
connected_slaves:0
master_replid:a54f3ba841c67762d6c1e33456c97b94c62f6ac0
master_replid2:e5c1ab2a68064690aebef4bd2bd4f3ddfba9cc27
master_repl_offset:4367
second_repl_offset:4368
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:4367
127.0.0.1:6480> 
複製代碼

執行完 slaveof no one 命令以後,6480 節點的角色立馬恢復成了 master ,咱們再來看看時候還和 6379 實例鏈接在一塊兒,咱們在 6379 節點上新增一個 key-value

127.0.0.1:6379> set y 3
OK
複製代碼

在 6480 節點上 get y

127.0.0.1:6480> get y (nil) 127.0.0.1:6480> 複製代碼

在 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
# Replication
role:slave
master_host:127.0.0.1
master_port:6480
master_link_status:up
master_last_io_seconds_ago:2
master_sync_in_progress:0
slave_repl_offset:4367
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:99624d4b402b5091552b9cb3dd9a793a3005e2ea
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:4367
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:4368
repl_backlog_histlen:0
127.0.0.1:6379> 
複製代碼

6379 節點的角色已是 slave 了,而且主節點的是 6480 ,咱們能夠再看看 6480 節點的 info replication

127.0.0.1:6480> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6379,state=online,offset=4479,lag=1
master_replid:99624d4b402b5091552b9cb3dd9a793a3005e2ea
master_replid2:a54f3ba841c67762d6c1e33456c97b94c62f6ac0
master_repl_offset:4479
second_repl_offset:4368
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:4479
127.0.0.1:6480> 
複製代碼

在 6480 節點上有 6379 從節點的信息,能夠看出 slaveof 命令已經幫咱們完成了主服務器的切換。

複製技術的原理

redis 的主從架構好像很簡單同樣,咱們就執行了一條命令就成功搭建了主從架構,而且數據複製也沒有問題,使用起來確實簡單,可是這背後 redis 仍是幫咱們作了不少的事情,好比主從服務器之間的數據同步、主從服務器的狀態檢測等,這背後 redis 是如何實現的呢?接下來咱們就一塊兒看看

數據複製原理

咱們執行完 slaveof 命令以後,咱們的主從關係就創建好了,在這個過程當中, master 服務器與 slave 服務器之間須要經歷多個步驟,以下圖所示:

redis 複製原理

slaveof 命令背後,主從服務器大體經歷了七步,其中權限驗證這一步不是必須的,爲了可以更好的理解這些步驟,就以咱們上面搭建的 redis 實例爲例來詳細聊一聊各步驟。

一、保存主節點信息

在 6480 的客戶端向 6480 節點服務器發送 slaveof 127.0.0.1 6379 命令時,咱們會立馬獲得一個 OK

127.0.0.1:6480> slaveof 127.0.0.1 6379
OK
127.0.0.1:6480> 
複製代碼

這時候數據複製工做並無開始,數據複製工做是在返回 OK 以後纔開始執行的,這時候 6480 從節點作的事情是將給定的主服務器 IP 地址 127.0.0.1 以及端口 6379 保存到服務器狀態的 masterhost 屬性和 masterport 屬性裏面

二、創建 socket 鏈接

在 slaveof 命令執行完以後,從服務器會根據命令設置的 IP 地址和端口,跟主服務器建立套接字鏈接, 若是從服務器可以跟主服務器成功的創建 socket 鏈接,那麼從服務器將會爲這個 socket 關聯一個專門用於處理複製工做的文件事件處理器,這個處理器將負責後續的複製工做,好比接受全量複製的 RDB 文件以及服務器傳來的寫命令。一樣主服務器在接受從服務器的 socket 鏈接以後,將爲該 socket 建立一個客戶端狀態,這時候的從服務器同時具備服務器和客戶端兩個身份,從服務器能夠向主服務器發送命令請求而主服務器則會向從服務器返回命令回覆。

三、發送 ping 命令

從服務器與主服務器鏈接成功後,作的第一件事情就是向主服務器發送一個 ping 命令,發送 ping 命令主要有如下目的:

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

在發送 ping 命令以後,正常狀況下主服務器會返回 pong 命令,接受到主服務器返回的 pong 回覆以後就會進行下一步工做,若是沒有收到主節點的 pong 回覆或者超時,好比網絡超時或者主節點正在阻塞沒法響應命令,從服務器會斷開復制鏈接,等待下一次定時任務的調度。

四、身份驗證

從服務器在接收到主服務器返回的 pong 回覆以後,下一步要作的事情就是根據配置信息決定是否須要身份驗證:

  • 若是從服務器設置了 masterauth 參數,則進行身份驗證
  • 若是從服務器沒有設置 masterauth 參數,則不進行身份驗證

在須要身份驗證的狀況下,從服務器將就向主服務器發送一條 auth 命令,命令參數爲從服務器 masterauth 選項的值,舉個例子,若是從服務器的配置裏將 masterauth 參數設置爲:123456,那麼從服務器將向主服務器發送 auth 123456 命令,身份驗證的過程也不是一路順風的,可能會遇到如下幾種狀況:

  • 從服務器經過 auth 命令發送的密碼與主服務器的 requirepass 參數值一致,那麼將繼續進行後續操做,若是密碼不一致,主服務將返回一個 invalid password 錯誤
  • 若是主服務器沒有設置 requirepass 參數,那麼主服務器將返回一個 no password is set 錯誤

全部的錯誤狀況都會令從服務器停止當前的複製工做,而且要從創建 socket 開始從新發起復制流程,直到身份驗證經過或者從服務器放棄執行復製爲止

五、發送端口信息

在身份驗證經過後,從服務器將執行 REPLCONF listening 命令,向主服務器發送從服務器的監聽端口號,例如在咱們的例子中從服務器監聽的端口爲 6480,那麼從服務器將向主服務器發送 REPLCONF listening 6480 命令,主服務器接收到這個命令以後,會將端口號記錄在從服務器所對應的客戶端狀態的 slave_listening_port 屬性了,也就是咱們在 master 服務器的 info replication 裏面看到的 port 值。

六、數據複製

數據複製是最複雜的一塊了,由 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 比較陌生,不要緊,咱們先來看看下面三個概念:

一、複製偏移量

參與複製的主從節點都會分別維護自身複製偏移量:主服務器每次向從服務器傳播 N 個字節的數據時,就將本身的偏移量的值加上 N,從服務器每次接收到主服務器傳播的 N個字節的數據時,將本身的偏移量值加上 N。經過對比主從服務器的複製偏移量,就能夠知道主從服務器的數據是否一致,若是主從服務器的偏移量老是相同,那麼主從數據一致,相反,若是主從服務器兩個的偏移量並不相同,那麼說明主從服務器並未處於數據一致的狀態,好比在有多個從服務器時,在傳輸的過程當中某一個服務器離線了,以下圖所示:

offset 不一致

因爲從服務器A 在數據傳輸時,因爲網絡緣由掉線了,致使偏移量與主服務器不一致,那麼當從服務器A 重啓而且與主服務器鏈接成功後,從新向主服務器發送 psync 命令,這時候數據複製應該執行全量複製仍是部分複製呢?若是執行部分複製,主服務器又如何補償從服務器A 在斷線期間丟失的那部分數據呢?這些問題的答案都在複製積壓緩衝區裏面

二、複製積壓緩衝區

複製積壓緩衝區是保存在主節點上的一個固定長度的隊列,默認大小爲 1MB,當主節點有鏈接的從節點(slave)時被建立,這時主節點(master) 響應寫命令時,不但會把命令發送給從節點,還會寫入複製積壓緩衝區,以下圖所示:

複製積壓緩衝區

所以,主服務器的複製積壓緩衝區裏面會保存着一部分最近傳播的寫命令,而且複製積壓緩衝區會爲隊列中的每一個字節記錄相應的複製偏移量。因此當從服務器從新連上主服務器時,從服務器經過 psync 命令將本身的複製偏移量 offset 發送給主服務器,主服務器會根據這個複製偏移量來決定對從服務器執行何種數據同步操做:

  • 若是從服務器的複製偏移量以後的數據仍然存在於複製積壓緩衝區裏面,那麼主服務器將對從服務器執行部分複製操做
  • 若是從服務器的複製偏移量以後的數據不存在於複製積壓緩衝區裏面,那麼主服務器將對從服務器執行全量複製操做

三、服務器運行ID

每一個 Redis 節點啓動後都會動態分配一個 40 位的十六進制字符串做爲運行 ID,運行 ID 的主要做用是用來惟一識別 Redis 節點,咱們可使用 info server 命令來查看

127.0.0.1:6379> info server
# Server
redis_version:5.0.5
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:2ef1d58592147923
redis_mode:standalone
os:Linux 3.10.0-957.27.2.el7.x86_64 x86_64
arch_bits:64
multiplexing_api:epoll
atomicvar_api:atomic-builtin
gcc_version:4.8.5
process_id:25214
run_id:7b987673dfb4dfc10dd8d65b9a198e239d20d2b1
tcp_port:6379
uptime_in_seconds:14382
uptime_in_days:0
hz:10
configured_hz:10
lru_clock:14554933
executable:/usr/local/redis-5.0.5/src/./redis-server
config_file:/usr/local/redis-5.0.5/redis.conf
127.0.0.1:6379> 
複製代碼

這裏面有一個run_id 字段就是服務器運行的ID

瞭解這幾個概念以後,咱們一塊兒來看看 psync 命令的運行流程,psync 命令運行流程以下圖所示:

psync 運行流程

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

一、從節點發送 psync 命令給主節點,參數 runId 是當前從節點保存的主節點運行ID,參數offset是當前從節點保存的複製偏移量,若是是第一次參與複製則默認值爲 -1。

二、主節點接收到 psync 命令以後,會向從服務器返回如下三種回覆中的一種:

  • 回覆 +FULLRESYNC {runId} {offset}:表示主服務器將與從服務器執行一次全量複製操做,其中 runid 是這個主服務器的運行 id,從服務器會保存這個id,在下一次發送 psync 命令時使用,而 offset 則是主服務器當前的複製偏移量,從服務器會將這個值做爲本身的初始化偏移量
  • 回覆 +CONTINUE:那麼表示主服務器與從服務器將執行部分複製操做,從服務器只要等着主服務器將本身缺乏的那部分數據發送過來就能夠了
  • 回覆 +ERR:那麼表示主服務器的版本低於 redis 2.8,它識別不了 psync 命令,從服務器將向主服務器發送 sync 命令,並與主服務器執行全量複製

七、命令持續複製

當主節點把當前的數據同步給從節點後,便完成了複製的創建流程。可是主從服務器並不會斷開鏈接,由於接下來主節點會持續地把寫命令發送給從節點,保證主從數據一致性。

通過上面 7 步就完成了主從服務器之間的數據同步,因爲這篇文章的篇幅比較長,關於全量複製和部分複製的細節就不介紹了,全量複製就是將主節點的當前的數據生產 RDB 文件,發送給從服務器,從服務器再從本地磁盤加載,這樣當文件過大時就須要特別大的網絡開銷,否則因爲數據傳輸比較慢會致使主從數據延時較大,部分複製就是主服務器將複製積壓緩衝區的寫命令直接發送給從服務器。

心跳檢測

心跳檢測是發生在主從節點在創建複製後,它們之間維護着長鏈接並彼此發送心跳命令,便之後續持續發送寫命令,主從心跳檢測以下圖所示:

主從心跳檢測

主從節點彼此都有心跳檢測機制,各自模擬成對方的客戶端進行通訊,主從心跳檢測的規則以下:

  • 主節點默認每隔 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
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6480,state=online,offset=25774,lag=0
master_replid:c62b6621e3acac55d122556a94f92d8679d93ea0
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:25774
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:25774
127.0.0.1:6379> 
複製代碼

能夠看出 slave0 字段的值最後面有一個 lag,lag 表示與從節點最後一次通訊延遲的秒數,正常延遲應該在 0 和 1 之間。若是超過 repl-timeout 配置的值(默認60秒),則斷定從節點下線並斷開復制客戶端鏈接,若是從節點從新恢復,心跳檢測會繼續進行。

主從拓撲架構

Redis的主從拓撲結構能夠支持單層或多層複制關係,根據拓撲復雜性能夠分爲如下三種:一主一從、一主多從、樹狀主從架構

一主一從結構

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

一主一從架構

一主一從架構用於主節點出現宕機時從節點 提供故障轉移支持,當應用寫命令併發量較高且須要持久化時,能夠只在從節點上開啓 AOF,這樣既保證數據安全性同時也避免了持久化對主節點的性能干擾。可是這裏有一個坑,須要你注意,就是當主節點關閉持久化功能時, 若是主節點脫機要避免自動重啓操做。由於主節點以前沒有開啓持久化功能自動重啓後數據集爲空,這時從節點若是繼續複製主節點會致使從節點數據也被清空的狀況,喪失了持久化的意義。安全的作法是在從節點上執行 slaveof no one 斷開與主節點的複製關係,再重啓主節點從而避免這一問題

一主多從架構

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

一主多從架構

一主多從架構能夠實現讀寫分離來減輕主服務器的壓力,對於讀佔比較大的場景,能夠把讀命令發送到 從節點來分擔主節點壓力。同時在平常開發中若是須要執行一些比較耗時的讀命令,如:keys、sort等,能夠在其中一臺從節點上執行,防止慢查詢對主節點形成阻塞從而影響線上服務的穩定性。對於寫併發量較高的場景,多個從節點會致使主節點寫命令的屢次發送從而過分消耗網絡帶寬,同時也加劇了主節點的負載影響服務穩定性。

樹狀主從架構

樹狀主從架構又稱爲樹狀拓撲架構,樹狀主從架構以下圖所示:

樹狀主從架構

樹狀主從架構使得從節點不但能夠複製主節 數據,同時能夠做爲其餘從節點的主節點繼續向下層複製。解決了一主多從架構中的不足,經過引入複製中 間層,能夠有效下降主節點負載和須要傳送給從節點的數據量。如架構圖中,數據寫入節點A 後會同步到 B 和 C節點,B節點再把數據同步到 D 和 E節點,數據實現了一層一層的向下複製。當主節點須要掛載多個從節點時爲了不對主節點的性能干擾,能夠採用樹狀主從結構下降主節點壓力。

最後

目前互聯網上不少大佬都有 Redis 系列教程,若有雷同,請多多包涵了。原創不易,碼字不易,還但願你們多多支持。若文中有所錯誤之處,還望提出,謝謝。

歡迎掃碼關注微信公衆號:「平頭哥的技術博文」,和平頭哥一塊兒學習,一塊兒進步。

平頭哥的技術博文
相關文章
相關標籤/搜索