redis雙主設計

redis雙主設計

背景

目前redis僅支持主從複製模式,能夠支持在線備份、讀寫分離等功能,實際應用中一般經過sentinel服務作主從切換的管理,這增長了管理的複雜度和維護成本,基於此360基礎架構組聯合DBA從redis內部實現了雙主功能。 redis

主從複製介紹

redis支持樹形的主從異步複製,並具備非阻塞、部分同步等特性,下面簡單介紹下其實現原理以及目前redis主從複製模式在故障發生時的數據丟失狀況。 緩存

實現原理

數據同步

slave開始鏈接到master時須要同步master已有的數據,具體過程以下圖所示,主要有如下幾個步驟: 網絡

  1. slave向master發送PSYNC命令,將緩存的master的runid和reploff發送給master。
  2. master 根據slave發送的reploff判斷須要開始同步的數據是否在當前緩衝區(積壓空間)中,若是在的話完成和slave的鏈接創建並向slave發送 CONTINUE回覆標誌,開始發送積壓空間中的數據;不然,向slave發送FULLSYNC標誌並開啓子進程dump當前時刻的快照數據到本地rdb 文件。
  3. slave接收psync命令的返回值,並判斷是CONTINUE仍是FULLSYNC,若是是CONTINUE則完成鏈接過程準備接收master數據;不然,觸發接收master dump的rdb文件事件,等待master發送rdb文件。
  4. master將快照數據dump到本地文件後,向slave發送rdb文件
  5. slave接收完master發送的rdb文件後,清空本地庫並從新加載接收的rdb文件,因爲這時的數據變化較大,若是開啓了aof選項則還需進行aof rewrite操做
  6. 開始傳播master積壓空間中的新數據。

數據傳播

master 實例會維護其slave實例列表,當有更改操做發生時,其會經過鏈接創建時建立的socket向全部slave實例發送操做命令進行數據傳播,同時爲了防 止故障恢復slave從新鏈接master時每次都進行全量同步,master實例會內部維護一個緩衝區(積壓空間)來緩存部分slave命 令,master數據傳播的具體過程爲: 架構

  1. 寫入本地庫
  2. 寫入積壓空間
  3. 觸發寫入slave實例事件,進行異步傳播;若是此刻正在進行數據同步操做則將命令寫入緩衝區,待同步操做完成後再觸發向slave實例寫入事件。

數據丟失

redis 主從模式並不能保證數據的100%完整,在網絡故障、主庫宕機等狀況下提高slave爲master時可能會丟失部分數據,以下圖所示,假如在某個時刻 master的數據還未徹底傳播給slave時,master宕機等狀況發生並將slave提高爲master,這時原來master未傳播的一部分數據 將丟失。 異步

雙主複製設計

前提假設

因爲時間、精力等因素,目前咱們在進行雙主設計時結合以下實際項目需求進行了一些設計折衷,後續咱們會繼續完善相關設計。 socket

  • 上層保證某個時間點只有一個master在寫 性能

  • 故障時容許丟失少許數據 spa

整體設計

爲 避免額外維護成本,雙主模塊徹底在redis內部實現,雙主兩個實例各自建立一個socket進行彼此通訊;加入雙主複製後不會影響原有的主從複製模式, 但以下圖所示,主從複製實例能夠經過咱們新增的doublemasterof命令轉化爲雙主複製,雙主複製實例也能夠經過原有的slaveof命令轉換爲 主從複製實例。 設計

同步策略

redis 雙主實例網絡故障恢復或重啓等狀況下會進行從新鏈接以同步彼此數據,主從模式下同步策略很簡單,只須要從庫同步主庫數據便可,而雙主模式下咱們必須根據一 定的策略來選出一個實例做爲數據同步的對象,咱們考慮到兩種具體同步策略:基於數據量和基於時間戳的同步策略。 對象

數據量同步策略

基 於數據量的同步策略能夠理解爲數據量少的實例去同步數據量多的實例,這種同步策略在故障發生時數據已經所有傳播到另一個實例的狀況下,故障恢復後能夠保 證數據完整性,但以下圖所示,假如原來操做在雙主A上執行,某一時刻雙主A上的操做還未同步到雙主B上發生了網絡故障,上層會切到B上繼續寫入,當寫入了 圖中紅色所示大小的數據後網絡故障恢復,這時進行數據同步時就有可能丟失A或B上的部分數據。

時間戳同步策略

對 於基於時間戳的同步策略,咱們會在redis內部維護雙主實例的最近更新時間戳,故障恢復進行數據同步時時間戳較舊的實例會同步時間戳較新的實例;和基於 數據量同步策略同樣當故障發生時若是數據已經所有傳播到另外實例則故障恢復後能夠保證數據完整性,不然,以下圖所示故障恢復後將丟失雙主A實例的部分數 據。

咱們的選擇

根據業務需求,咱們須要保證最新寫入的數據不會丟失,因此具體實現上咱們選擇了基於時間戳的同步策略。

同步實現

咱們在redis原有的全同步,部分同步的基礎上增長了ignore resync策略以實現雙主同步,具體實現以下圖所示,有如下幾個步驟:

  1. 雙主實例A連接到另一個實例B時會經過發送2mastersync命令,該命令會將A實例最後的更新時間戳發送給B
  2. B 實例判斷A的最後更新時間戳是否比本身新,若是比本身新則直接將A標示爲ONLINE並向A發送IGNORE runid reploff信息,A接收到IGNORE信息後記錄下runid reploff,便可完成與B的同步;不然,則按照主從同步方式進行全同步或部分同步。

數據傳播

對 一個雙主實例的更改操做,redis內部會經過雙主實例創建鏈接時建立的socket異步傳播給另外一個雙主實例,這裏要解決的問題是要避免數據再次傳播回 來,具體實現上咱們經過雙主實例的runid進行判斷,每一個雙主實例內部會維護其另一個實例的runid信息,當有更改命令要執行時,咱們會經過 runid來判斷該命令是不是其雙主實例傳播過來的,若是是將再也不回傳。

複製偏移量維護

redis主從實現機制上,當通訊模塊接收到主庫的更改命令時會直接在從庫上增長其複製偏移量來記錄數據同步的位置,而對於雙主實例咱們知道爲避免數據循環傳播,雙主實例A傳播給雙主實例B的命令不會回傳過來,那麼該如何維護其複製偏移量呢?設計上咱們考慮了兩種策略:

策略一

以下圖所示,雙主A向雙主B傳播一條數據後,B會回覆A一個ACK length確認,A接收到確認信息後將本身的複製偏移量增長length。

策略二

以下圖所示,雙主A再向B傳播數據以前本身主動增長複製偏移量,雙主B不會向雙主A回覆確認信息

Imgur

策略一對比策略二進一步保證了數據的完整性,但同時帶來了必定的網絡開銷,兩種策略都不能徹底保證複製偏移量再網路故障下的正確性(策略一在ACK丟失的狀況下沒法保證複製偏移量正確),結合目前的需求爲了避免影響性能咱們選擇了策略二。

小結

結合目前項目的需求咱們在redis內部實現了雙主功能,可是也有一些須要改進的地方,歡迎你們提出意見,後面咱們會不斷完善,敬請關注!


http://weibo.com/p/1001603872517453663800

相關文章
相關標籤/搜索