本文章首發自本人公衆號:壹枝花算不算浪漫,如若轉載請標明來源!java
感興趣的小夥伴可關注我的公衆號:壹枝花算不算浪漫node
前面已經學習了Redis的持久化方式,接下來開始學習Redis主從架構的原理,來看看Redis如何利用主從架構來保證高併發的。redis
單機的redis通常QPS不會超過超過10萬+,通常單機QPS都在幾萬左右,若是須要支撐高併發,咱們能夠將Redis作成主從架構來支持讀寫分離。數據庫
主從架構 -> 讀寫分離 -> 支撐10萬+讀QPS緩存
當啓動一個slave node的時候,它會發送一個PSYNC命令給master node網絡
若是這是salve node重複你給你鏈接master node,那麼master node僅僅會複製給slave部分缺失的數據;不然若是是slave node第一次鏈接master node,那麼會觸發一次full resynchronization架構
開始full resynchronization的時候,master會啓動一個後臺線程,開始生成一份RDB快照文件,同時還會將從客戶端收到的全部寫命令緩存在內存中。併發
RDB文件生成完畢以後,master將這個RDB發送給slave,salve會先寫入本地磁盤,而後再從本地磁盤加載到內存中。less
接着master會將內存中緩存的寫命令發送給slave,slave也會同步這些數據。異步
slave node若是跟master node有網絡故障,斷開了鏈接,會自動重連。
從redis 2.8以後,就支持主從複製的斷點續傳,若是主從複製過程當中,網絡鏈接斷掉了,那麼能夠接着上次複製的地方,繼續複製下去,而不是從頭開始複製一份
master node會在內存中建立一個backlog,master和slave都會保存一個replica offset還有一個master id,offset就是保存在backlog中的。若是master和slave網絡鏈接斷掉了,slave會讓master從上次的replica offseet開始繼續複製
可是若是沒有找到對應的offset,那麼就會執行一次full resynchronization
master在內存中直接建立rdb,而後發送給slave,不會在本身的本地落地磁盤了
// 默認不使用diskless同步方式,能夠改爲yes
repl-diskless-sync yes
// 無磁盤diskless方式在進行數據傳遞以前會有一個時間的延遲,以便slave端可以進行到待傳送的目標隊列中,這個時間默認是5秒
repl-diskless-sync-delay 5
複製代碼
slave不會過時key,只會等待master過時key。若是master過時了一個key,或者經過LRU淘汰了一個key,那麼會模擬一條del命令發送個slave。
以上的執行流程如圖:
具體流程以下:(如下內容參考自: blog.csdn.net/houjixin/ar…)
全備份過程當中,在slave啓動時,會向其master發送一條SYNC消息,master收到slave的這條消息以後,將可能啓動後臺進程進行備份,備份完成以後就將備份的數據發送給slave,初始時的全同步機制是這樣的:
上述函數調用過程以下圖1所示:
Redis的正常部署中通常都是一個master用於寫操做,若干個slave用於讀操做,另外按期的數據備份操做也是單獨選址一個slave完成,這樣能夠最大程度發揮出redis的性能。在部署完成,各master\slave程序啓動以後,首先進行第一階段初始化時的全同步操做,全同步操做完成以後,後續全部寫操做都是在master上進行,全部讀操做都是在slave上進行,所以用戶的寫操做須要及時擴散到全部的slave以便保持數據最大程度上的同步。Redis的master-slave進程在正常運行期間更新操做(包括寫、刪除、更改操做)的同步方式以下:
master接收到一條用戶的操做後,將調用函數call函數來執行具體的操做函數(此過程可參考另外一文檔《redis命令執行流程分析》),在該函數中首先經過proc執行操做函數,而後將判斷操做是否須要擴散到各slave,若是須要則調用函數propagate()來完成此操做。
propagate()函數完成將一個操做記錄到aof文件中或者擴散到其餘slave中;在該函數中經過調用feedAppendOnlyFile()將操做記錄到aof中,經過調用replicationFeedSlaves()將操做擴散到各slave中。
函數feedAppendOnlyFile()中主要保存操做到aof文件,在該函數中首先將操做轉換成redis內部的協議格式,並以字符串的形式存儲,而後將字符串存儲的操做追加到aof文件後。
函數replicationFeedSlaves()主要將操做擴散到每個slave中;在該函數中將遍歷本身下面掛的每個slave,以此對每一個slave進行以下兩步的處理:將slave的數據庫切換到本操做所對應的數據庫(若是slave的數據庫id與當前操做的數據id不一致時才進行此操做);將命令和參數按照redis的協議格式寫入到slave的回覆緩存中。寫入切換數據庫的命令時將調用addReply,寫入命令和參數時將調用addReplyMultiBulkLen和addReplyBulk,函數addReplyMultiBulkLen和addReplyBulk最終也將調用函數addReply。
在函數addReply中將調用prepareClientToWrite()設置slave的socket寫入事件處理函數sendReplyToClient(經過函數aeCreateFileEvent進行設置),這樣一旦slave對應的socket發送緩存中有空間寫入數據,即調用sendReplyToClient進行處理。
函數sendReplyToClient()的主要功能是將slave中要發送的數據經過socket發出去。
第一次slave鏈接master的時候,執行的是全量複製,這個過程當中有些細節的機制
master和slave都會維護一個offset
master會在自身不斷累加offset,slave也會在自身不斷累加offset。slave每秒都會上報本身的offset給master,同時master也會保存每一個slave的offset。
offset並非只用在全量複製中,主要是master和slave都要知道各自的數據offset,才能知道互相之間數據不一致的狀況
backlog機制
master node有一個backlog,默認大小是1M,master node給slave node複製數據時,也會將數據backlog中同步寫一份,backlog主要是用來作全量複製中斷時候的增量複製
master run id
在redis中執行info server命令,能夠看到master run id,若是根據host+ip定位master node,是不許確的,若是master node重啓或者數據出現了變化,那麼slave node應該根據不一樣的run id區分,run id不一樣就作全量複製。 若是須要不更改run id重啓redis,可使用redis-cli debug reload命令
psync命令
從節點使用psync從master node進行復制,psync runid offset,master node會根據自身的狀況返回響應信息,多是FULLRESYNC runid offset觸發全量複製,多是CONTINUE觸發增量複製
heatbeat機制
主從節點互相都會發送heartbeat信息,master默認每隔10秒發送一次heartbeat,slave node每隔1秒發送一個heartbeat