主從庫與切片叢集機制
主從複製原始碼剖析
redis的主從複製主要包括全量複製RDB檔案,增量複製,長連線同步,使用了基於狀態機的設計思想,來實現不同狀態和狀態間的跳轉
基於狀態機實現的話,在開發程式時只需要考慮不同狀態下具體要執行的操作,以及狀態之間的跳轉條件即可
四大階段
初始化階段:將例項A設定為B的從庫,獲取主庫的ip和port
建立連線階段:例項A嘗試與主庫建立TCP連線,並在連線上監聽主庫的命令
主從握手階段:主從庫間相互傳送 PING-PONG 訊息,同時從庫根據配置資訊向主庫進行驗證
複製型別判斷和執行階段:主庫會根據從庫傳送的命令引數作出相應的三種回覆,分別是執行全量複製、執行增量複製、發生錯誤。最後,從庫在收到上述回覆後,就會根據回覆的複製型別,開始執行具體的複製操作
狀態機實現
在每個redis例項裡都對應著一個redisServer結構體,在其中與主從複製狀態機相關的變數是repl_state
初始化階段
例項啟動後,把狀態機初始狀態設定為REPL_STATE_NONE,而一旦執行了replicaof masterip masterport之後,會設定主庫IP和埠號,並把狀態機設定為REPL_STATE_CONNECT,完成初始化階段
建立連線階段
redis會每隔1000ms呼叫replicationCron()執行任務,檢查複製狀態機狀態為REPL_STATE_CONNECT的話就開始和主庫建立連線,然後在連線上建立讀寫事件並且註冊處理讀寫事件的函式syncWithMater,然後將從庫狀態機設定為REPL_STATE_CONNECTING
主從握手階段
一旦主從庫間連線建立,從庫例項中的syncWithMaster函式被回撥,在該函式中如果從庫例項狀態為REPL_STATE_CONNECTING,就會傳送PING訊息給主庫,並設定狀態機為REPL_STATE_RECEIVE_PONG
然後從庫會依次傳送驗證資訊、埠號、IP、對RDB檔案和無盤複製的支援情況,每一次握手傳送資訊時都對應著從庫的一組狀態變遷(傳送前是SEND狀態,傳送完成後是RECEIVE狀態並開始讀取主庫返回的結果)
複製型別判斷與執行階段
完成握手之後,從庫狀態變遷為REPL_STATE_SEND_PSYNC,表明開始向主庫傳送PSYNC命令,開始實際的資料同步,當呼叫函式完成傳送之後又變遷為REPL_STATE_RECEIVE_PSYNC,這個呼叫函式會負責向主庫傳送資料同步的命令,並根據主庫的回覆訊息將返回值置為不同結果
然後syncWithMaster根據返回值的不同執行不同處理,比如返回FULLRESYNC,從庫會在和主庫的網路連線上註冊readSyncBulkPayload函式,並將狀態機設定為REPL_STATE_TRANSFER,表明開始實際的資料同步
哨兵和raft
redis哨兵leader選舉實現時涉及到raft協議,區別在於:在正常執行時,不同例項間不是leader和follower的關係,而是對等的關係
實現哨兵例項工作主題邏輯的函式:sentinelHandleRedisInstance函式,週期性執行檢測哨兵監聽節點的狀態
- 重建連線:嘗試和斷連的例項重建連線
- 傳送命令:向例項傳送ping、info等命令
- 判斷主觀下線:檢查監聽的例項是否主觀下線
- 判斷客觀下線和執行故障切換:首先真對監聽的主節點判斷是否客觀下線;接著判斷是否要啟動故障切換,如果需要就再獲取其他哨兵對主節點狀態的判斷,並向其他哨兵傳送is-master-down-by-addr命令發起leader選舉,隨後執行故障切換,最後再獲取對新主節點的狀態判斷
判斷主節點是否客觀下線的函式邏輯:透過遍歷監聽同一主節點的其他哨兵的 flags 變數,來判斷主節點是否客觀下線的。
隨後判斷是否要進行故障切換的條件有三個:
- 主節點的 flags 已經標記了 SRI_O_DOWN;
- 當前沒有在執行故障切換;
- 如果已經開始故障切換,那麼開始時間距離當前時間,需要超過 sentinel.conf 檔案中的 sentinel failover-timeout 配置項的 2 倍。
同時sentinelAskMasterStateToOtherSentinels用於給其他哨兵傳送 sentinel is-master-down-by-addr,命令包括主節點ip,主節點port,當前紀元和例項ID,如果例項ID不是*,則會呼叫sentinelVoteLeader進行哨兵的Leader選舉
假設哨兵A判斷主節點master客觀下線,它向B發起投票請求,B執行sentinelVoteLeader時會判斷A、B、master記錄的leader紀元,透過紀元來進行輪次記錄從而讓follower在一輪中只能投一票,只有master的leader紀元小於哨兵A紀元,且B紀元小於等於A紀元,保證B還沒有投過票,才能給A投票
最終leader當選的條件是:獲得超過半數的,且超過預設的quorum閾值的其他哨兵贊成票