面試官:聊聊Redis如何用主從複製模式 實現高可用

作者:繪你一世傾城 鏈接:https://juejin.im/post/5e755f0d518825490e458453

面試官:聊聊Redis如何用主從複製模式 實現高可用


之前總結過redis的持久化機制:深度剖析Redis持久化機制,持久化機制主要解決redis數據單機備份問題;redis的高可用需要考慮數據的多機備份,多機備份通過主從複製來實現,這是redis高可用的基石。本文將詳細介紹redis主從複製的實現原理,在使用過程中應該注意的問題和相關配置。

1. CAP理論

CAP理論是分佈式領域的牛頓定律,所有的分佈式存儲中間件都要使用它作為理論基石。如下圖所示:

面試官:聊聊Redis如何用主從複製模式 實現高可用

這個原理很簡單,首先明確幾個概念:

  • C : Consistent, 一致性
  • A : Availability, 可用性
  • P : Partition tolerance, 分區容忍性

分佈式系統的節點往往分佈在不同的機器上,它們之間由網絡進行隔離,當網絡斷開時就會產生網絡分區。網絡分區不可避免,但是當網絡分區發生時,對分佈式系統中一個節點的修改操作無法同步給其它節點,數據一致性也就無法滿足;想要滿足一致性,除非犧牲可用性,也就是暫停分佈式節點服務,等到網絡恢復,數據一致後,再對外提供服務。分佈式系統中網絡分區不可避免,一致性和可用性水火不容。這就是cap理論:網絡分區發生時,一致性和可用性兩難全


面試官:聊聊Redis如何用主從複製模式 實現高可用


2.redis主從複製

2.1 概述

互聯網圈經常談“三高”架構:高併發、高性能、高可用。對於redis來說,高併發、高性能可以保證,高可用需要架構的設計,單機redis由於存在系統崩潰、硬盤故障的風險,還有內存的限制,所以企業一般都會搭建主從,對系統有更高要求會搭建集群。

面試官:聊聊Redis如何用主從複製模式 實現高可用


為了保持數據一致性,主節點(master)只寫,從節點(slave)只讀,數據由主節點複製給從節點,這個複製的過程就是主從複製

redis的主從複製是異步的,分佈式的redis系統並不滿足一致性要求,但是在網絡斷開的情況下,主節點依然可以對外提供服務,滿足可用性。redis保證最終一致性,從節點會努力追趕主節點,最終從節點的狀態會和從節點保持一致。網絡斷開的情況下,主從節點數據會出現大量的不一致,但一旦網絡恢復,從節點會繼續追趕主節點,最終達到和主節點狀態一致。

為了減輕redis主節點的同步負擔,redis 的後續版本還增加了從從同步,與此同時,數據一致性會變差。

面試官:聊聊Redis如何用主從複製模式 實現高可用


2.2 主從複製的作用

主從複製在服務中起到了什麼效果呢?

  • 讀寫分離:master寫,slave讀,提高服務器的讀寫負載能力。
  • 負載均衡:基於主從架構,配合讀寫分離,由slave分擔master負載,並根據需求的變化,改變slave的數量,通過多個從節點分擔數據讀取負載,大大提高redis服務器併發量和數據吞吐量。
  • 故障恢復:當master出現問題時,由slave提供服務,實現快速的故障恢復。
  • 數據冗餘:實現數據熱備份,是持久化之外的一種數據冗餘方式。
  • 高可用基石:基於主從複製,構建哨兵模式與集群,實現redis的高可用方案。

2.3 怎樣配置實現主從複製?

有三種配置實現redis的主從:

  • 方式一:客戶端發送命令
<code>slaveof <masterip> <masterport>
複製代碼/<masterport>/<masterip>/<code>
  • 方式二:啟動服務器時添加參數
<code>redis-server -slaveof <masterip> <masterport>
複製代碼/<masterport>/<masterip>/<code>
  • 方式三:服務器配置文件中配置(通過redis.conf)
<code>slaveof <masterip> <masterport>
複製代碼/<masterport>/<masterip>/<code>

比較主流的用法是通過配置文件的方式實現主從。還有其它的一些命令:

  • 主從斷開連接,可以從客戶端發送命令
<code>slaveof no one
複製代碼/<code>
  • 服務端設置了授權訪問
<code>#master有兩種方式設置
//-- 1.master配置文件中設置
requirepass <password>
//-- 2.master客戶端發送命令設置密碼
config set requirepass <password>

config get requirepass

#slave有三種方式實現認證
//-- 1.客戶端發送命令設置密碼
auth <password>
//-- 2.slave配置文件設置密碼
masterauth <password>
//-- 3.啟動客戶端設置密碼
redis-cli -a <password>
複製代碼/<password>/<password>/<password>/<password>/<password>/<code>

2.4 redis主從複製的工作流程

redis主從複製實現過程有三個階段:


面試官:聊聊Redis如何用主從複製模式 實現高可用


建立連接、數據同步、命令傳播。建立連接階段主從節點建立通信的橋樑,彼此之間同步一些基礎信息;數據同步階段實現從節點全量同步主節點的數據;從節點同步完主節點數據之後,就進入了命令傳播階段,主節點接收寫請求,數據不斷髮生變化,通過命令傳播階段主節點將數據源源不斷的同步給從節點。下邊我們詳細介紹主從複製這三個階段的工作細節和注意事項。

2.4.1 建立連接階段

建立slave到master的連接,使master能識別slave, 並保存slave的端口號;與此同時,slave也保存master的地址和端口號信息。

面試官:聊聊Redis如何用主從複製模式 實現高可用

  • slave發送slaveof ip port命令給master,master響應slave
  • slave保存master的ip和端口號,建立socket連接
  • 在socket連接之上,主從節點實現了心跳機制,這部分內容也比較重要,後邊會提到。
  • 如果有認證機制,從節點通過上邊說到的認證指令,發送認證信息給master,實現認證。
  • 從節點將自己的端口信息發送發送給主節點,主節點保存。

通過以上過程主從之間的連接就建立了。

2.4.2 數據同步階段

數據同步階段實現的功能是從節點從主節點同步全量的數據。這個過程又分為幾個小階段,最主要的就是數據的全量複製和部分複製, 對應的流程就是主節點發送rdb文件同步數據和發送緩衝區寫命令(aof)同步數據給從節點。下圖是實現細節:


面試官:聊聊Redis如何用主從複製模式 實現高可用


  • 首先slave節點先發起命令psync ? -1,向master節點要全量數據。
  • master節點接收到指令以後,執行bgsave,將當前內存數據快照保存為rdb文件,這個過程為了不影響主節點繼續對外提供服務,採用了Copy On Write技術。與此同時,master節點也會將bgsave保存快照期間接收到的寫更新命令添加到複製擠壓緩衝區當中。master節點rdb文件生成完畢以後,會通過第一階段建立的socket連接將它發送給slave節點,還會發送+FULLRESYNC runid offset給slave節點,告訴slave節點自己的runid和offset。

什麼是runid?

redis-server在每次啟動的時候都會生成一個runid,因為redis-server是一個守護進程,所以在運行期間,runid不會發生變化,可以通過info server指令查看runid,它是一個40位字符長度的字符串。上文提到的psync有兩個參數,和+FULLRESYNC一樣:psync <runid> <offset>;runid的意義是什麼呢?當master節點發生故障發生了變更後,在接到slave的指令以後,對比參數中的runid如果和自己的runid不一致,就會再次進行全量複製,因為換主了。/<offset>/<runid>

什麼是複製擠壓緩衝區和offset?

複製擠壓緩衝區是一個先進先出(FIFO)的環形隊列,用於存儲服務端執行過的命令,每次傳播命令,master節點都會將傳播的命令記錄下來,保存在這裡。

複製擠壓緩衝區由兩部分組成:偏移量和字節值。字節值是redis指令字節的存儲(redis指令以一種Redis序列化文本協議的格式存儲),偏移量offset就是當前字節值在環形隊列中的偏移量。


  • slave節點接收完master節點同步的rdb文件之後,將rdb的內容加載到自己的內存,然後將master節點的runid和offset記錄下來。
  • 有了master節點的runid和offset,在加載完rdb文件之後,就開始向master節點發送新的命令psync runid offset,向master節點要新數據。新數據是master節點在bgsave生成rdb文件時和向slave同步數據的這段時間產生的,所以這段時間的工作也稱為部分複製。
  • master節點收到slave節點發送的請求數據命令之後,會檢查runid是否一致(是否換主),offset是否一致(因為複製擠壓緩衝區是定長的,所有有可能會溢出),這兩個條件只要有一個不滿足,master就會向slave再次全量的同步數據(讀者可能會發現,如果master節點寫併發很高,複製擠壓緩衝區又設置的比較小的話,可能會每次向slave同步完數據以後,每次複製擠壓緩衝區都會溢出,造成主從之間循環的全量複製。這確實是應該規避的問題!我們後邊會針對主從複製應該考慮的問題做一個總結)。在runid和offset都滿足的情況下,master節點就會向slave節點發送指令+CONTINUE offset,接著從offset位置開始同步數據,數據都在主節點的複製擠壓緩衝區中了,所以直接複製發送就可以了。
  • slave節點接收到master節點發送的+CONTINUE offset指令之後,更新自己保存的offset值,然後將從master節點同步過來的數據,使用bgrewriteaof,重放aof數據。

到這裡,主從複製的第二階段:數據同步階段工作就完成了。

2.4.3 命令傳播階段

命令傳播階段類似於數據同步階段的部分複製,當master節點數據被修改以後,就和slave節點的數據不一致了,這個時候master節點就會根據slave上報的offset開始傳播數據(一主多從的架構中,master節點要記錄每一個slave的offset)。slave接收到數據以後,執行bgrewriteaof重放數據。在這個工作過程中,如果因為網路問題導致offset溢出或者換主的情況,主從之間還是會進行數據的全量同步的。

2.5 心跳機制

進入命令傳播階段以後,master節點與slave節點需要進行信息傳遞,使用心跳機制進行維護,實現雙方保持在線。

master節點心跳使用指令PING,由配置repl-ping-slave-period決定,默認10秒,作用是判斷slave是否在線,可以通過info replication獲取slave最後一次連接到現在的時間間隔,lag的值維護在0和1視為正常。

slave節點的心跳任務使用指令REPLCONF ACK {offset},週期是1秒,slave的心跳任務有兩個作用:

  • 彙報自己的offset給master,這在數據傳播起到了關鍵性作用,因為master節點向slave節點傳播數據,offset是一項非常重要的指標。
  • 判斷master是否在線

在心跳階段應該注意:當slave節點多數掉線,或者延遲過高時,master節點為了保證數據的穩定性,將拒絕所有信息的同步。有如下配置:

<code>min-slaves-to-write 2
min-slaves-max-lag 8
複製代碼/<code>

上述配置含義是:當slave數量小於2個,或者所有的slave的延遲都大於等於8秒時,強制關閉master寫功能,停止數據同步。

3.主從複製常見問題

上邊介紹的主從複製是建立在主從節點間的網絡和服務都正常的情況下,業務場景中要考慮更多的實際情況。

3.1 master重啟

伴隨著系統的運行,master節點的內存數據量變得很大的情況下,一旦master節點重啟,runid將發生變化,會導致slave的全量複製操作。

這裡有一個優化方案:在master節點內部創建master_replid變量,使用runid相同的策略生成,長度41位,發送給所有的slave節點。在master節點關閉時,執行命令shutdown save,進行RDB的數據持久化,將runid與offset保存在RDB文件中。在RDB文件中有了repl-id和repl-offset信息以後,通過指令redis-check-rdb命令可以查看這些信息。在master節點重啟後,將RDB文件加載到內存中以後,也會將repl-id和repl-offset加載到內存中。通過info 指令可以查看:

<code>master_repl_id = repl
master_repl_offset = repl-offset
複製代碼/<code>

作用是:master節點重啟之後會保存原來的runid,重啟後恢復該值,會讓所有的slave節點認為還是之前的master節點。

3.2 複製積壓緩衝區太小

當複製積壓緩衝區太小的時候,當master節點寫併發很大,master節點和slave節點網絡有抖動的時候,就會導致數據同步不及時,造成offset溢出,進而導致全量複製。這個時候,我們可以考慮修改複製積壓緩衝區的大小,由配置repl-backlog-size控制。設置多大比較合適呢,這要根據master的併發量和網絡情況做具體的評估。

3.3 slave執行了keys * 、hgetall等命令

前邊內容我們提到slave節點每秒都會發送REPLCONF ACK指令到master節點,master節點調用複製函數relicationCron()同步數據給slave節點時,如果slave節點執行了keys *、hgetall等阻塞命令的時候,就會在很長一段時候得不到響應。這就會導致master的各種資源(輸出緩衝區、帶寬、連接)等被佔用。master節點的CPU就會變高,slave頻繁的斷開連接。

解決方案是master節點通過配置:repl-timeout設置合理的超時時間(默認60s),超過改值,master節點將釋放slave節點。

3.4 master節點發送ping指令頻度低,網絡存在丟包

master節點默認10s向slave節點發送一次ping指令,因為master節點不僅要處理大量的寫任務,還可能維護著多個master,所以ping設置的不太及時。但是當ping指令在網絡中存在丟包時,master節點如果設置的超時時間太短,就會導致master節點與slave節點斷開連接。

解決方案有:提高master節點ping的頻度,超時時間repl-time設置為ping指令時間的5~10倍。

3.5 網絡信息不同步,數據發送有延遲

當主從同步中網絡數據發送有延遲的時候,就會造成多個slave獲取到的數據不同步,解決方案是優化master節點和slave節點的網絡環境,通常是放置在一個機房部署。另外要監控master和slave節點的延遲,如果延遲過大,可以暫時屏蔽對slave節點的訪問。通過下面指令設置:

<code>slave-serve-stale-data yes | no
複製代碼/<code>

開啟後,slave節點僅僅能響應info、slaveof等少數命令,除非對數據一致性要求很高,否則不要輕易這樣使用。

4.總結

本文主要總結了redis實現主從複製的實現細節和注意事項。redis的主從複製是實現高可用的重要基石,後邊的文章將總結哨兵和集群的搭建。


小福利:

通過合法手段,獲取到一些極客付費課程 ,噓~,免費 送給小夥伴們。評論【666】我會私信發你

面試官:聊聊Redis如何用主從複製模式 實現高可用


分享到:


相關文章: