Redis學習二:Redis高並發之主從模式

申明

本文章首發自本人公眾號:壹枝花算不算浪漫,如若轉載請標明來源!

感興趣的小夥伴可關注個人公眾號:壹枝花算不算浪漫

22.jpg22.jpg

前言

前面已經學習了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會先寫入本地磁碟,然後再從本地磁碟載入到記憶體中。

接著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

過期key處理

slave不會過期key,只會等待master過期key。如果master過期了一個key,或者通過LRU淘汰了一個key,那麼會模擬一條del命令發送個slave。

以上的執行流程如圖:

image.jpgimage.jpg

複製的完整流程

  1. slave node在redis.conf中的slaveof配置master的host資訊,slave node啟動,僅僅是保存了master node資訊,此時複製流程並未開始
  2. slave node內部有個定時任務,每秒檢查是否有新的master node需要連接和複製,如果發現,就跟master node建立socket網路連接
  3. slave node發送ping的命令給master node
  4. 口令認證,如果master設置了requiresspass,那麼slave node必須發送masterauth的口令過去認證
  5. master node第一次執行全量複製,將所有數據發送給slave node
  6. master node後續持續將寫命令,非同步複製給slave node

具體流程如下:(以下內容參考自:
//blog.csdn.net/houjixin/article/details/27680183)

全量複製

全備份過程中,在slave啟動時,會向其master發送一條SYNC消息,master收到slave的這條消息之後,將可能啟動後台進程進行備份,備份完成之後就將備份的數據發送給slave,初始時的全同步機制是這樣的:

  1. slave啟動後向master發送同步指令SYNC,master接收到SYNC指令之後將調用該命令的處理函數syncCommand()進行同步處理;
  2. 在函數syncCommand中,將調用函數rdbSaveBackground啟動一個備份進程用於數據同步,如果已經有一個備份進程在運行了,就不會再重新啟動了。
  3. 備份進程將執行函數rdbSave()完成將redis的全部數據保存為rdb文件。
  4. 在redis的時間事件函數serverCron(redis的時間處理函數是指它會定時被redis進行操作的函數)中,將對備份後的數據進行處理,在serverCron函數中將會檢查備份進程是否已經執行完畢,如果備份進程已經完成備份,則調用函數backgroundSaveDoneHandler完成後續處理。
  5. 在函數backgroundSaveDoneHandler中,首先更新master的各種狀態,例如,備份成功還是失敗,備份的時間等等。然後調用函數updateSlavesWaitingBgsave,將備份的rdb數據發送給等待的slave。
  6. 在函數updateSlavesWaitingBgsave中,將遍歷所有的等待此次備份的slave,將備份的rdb文件發送給每一個slave。另外,這裡並不是立即就把數據發送過去,而是將為每個等待的slave註冊寫事件,並註冊寫事件的響應函數sendBulkToSlave,即當slave對應的socket能夠發送數據時就調用函數sendBulkToSlave(),實際發送rdb文件的操作都在函數sendBulkToSlave中完成。
  7. sendBulkToSlave函數將把備份的rdb文件發送給slave。

上述函數調用過程如下圖1所示:

image.pngimage.png

數據修改操作的同步

Redis的正常部署中一般都是一個master用於寫操作,若干個slave用於讀操作,另外定期的數據備份操作也是單獨選址一個slave完成,這樣可以最大程度發揮出redis的性能。在部署完成,各master\slave程式啟動之後,首先進行第一階段初始化時的全同步操作,全同步操作完成之後,後續所有寫操作都是在master上進行,所有讀操作都是在slave上進行,因此用戶的寫操作需要及時擴散到所有的slave以便保持數據最大程度上的同步。Redis的master-slave進程在正常運行期間更新操作(包括寫、刪除、更改操作)的同步方式如下:

  1. master接收到一條用戶的操作後,將調用函數call函數來執行具體的操作函數(此過程可參考另一文檔《redis命令執行流程分析》),在該函數中首先通過proc執行操作函數,然後將判斷操作是否需要擴散到各slave,如果需要則調用函數propagate()來完成此操作。

  2. propagate()函數完成將一個操作記錄到aof文件中或者擴散到其他slave中;在該函數中通過調用feedAppendOnlyFile()將操作記錄到aof中,通過調用replicationFeedSlaves()將操作擴散到各slave中。

  3. 函數feedAppendOnlyFile()中主要保存操作到aof文件,在該函數中首先將操作轉換成redis內部的協議格式,並以字元串的形式存儲,然後將字元串存儲的操作追加到aof文件後。

  4. 函數replicationFeedSlaves()主要將操作擴散到每一個slave中;在該函數中將遍歷自己下面掛的每一個slave,以此對每個slave進行如下兩步的處理:將slave的資料庫切換到本操作所對應的資料庫(如果slave的資料庫id與當前操作的數據id不一致時才進行此操作);將命令和參數按照redis的協議格式寫入到slave的回復快取中。寫入切換資料庫的命令時將調用addReply,寫入命令和參數時將調用addReplyMultiBulkLen和addReplyBulk,函數addReplyMultiBulkLen和addReplyBulk最終也將調用函數addReply。

  5. 在函數addReply中將調用prepareClientToWrite()設置slave的socket寫入事件處理函數sendReplyToClient(通過函數aeCreateFileEvent進行設置),這樣一旦slave對應的socket發送快取中有空間寫入數據,即調用sendReplyToClient進行處理。

  6. 函數sendReplyToClient()的主要功能是將slave中要發送的數據通過socket發出去。

image.pngimage.png

數據同步相關核心機制

第一次slave連接master的時候,執行的是全量複製,這個過程中有些細節的機制

    1. master和slave都會維護一個offset

      master會在自身不斷累加offset,slave也會在自身不斷累加offset。slave每秒都會上報自己的offset給master,同時master也會保存每個slave的offset。

      offset並不是只用在全量複製中,主要是master和slave都要知道各自的數據offset,才能知道互相之間數據不一致的情況

    2. backlog機制

      master node有一個backlog,默認大小是1M,master node給slave node複製數據時,也會將數據backlog中同步寫一份,backlog主要是用來做全量複製中斷時候的增量複製

    3. 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命令

    4. psync命令

      從節點使用psync從master node進行複製,psync runid offset,master node會根據自身的情況返迴響應資訊,可能是FULLRESYNC runid offset觸發全量複製,可能是CONTINUE觸發增量複製

    5. heatbeat機制

      主從節點互相都會發送heartbeat資訊,master默認每隔10秒發送一次heartbeat,slave node每隔1秒發送一個heartbeat

Tags: