1. Redis 集群
1.1 集群節點
- 加入集群:
- 節點使用
cluster meet <ip> <port>
命令來嘗試握手, 若成功, 則加入到相應的集群中.
- 節點使用
- ClusterNode
- 節點使用ClusterNode 來記錄自己的狀態, 并為集群中所有其它節點創建相應的clusterNode.
ClusterNode{ ctime, name, flags, configEpoch, ip, port, clusterLink};
ClusterLink{ ctime, fd, sndbuf, rcvbuf, ClusterNode*};
ClusterState{ mySelf, currentEpoch; state, size, dict* nodes}.
- flags 記錄著Node 的角色加狀態信息.
- fd 代表tcp 描述符.
- sndbuf 和rcvbuf 作為緩沖區 ,保存著從其它Node 發送和接收到的消息.
- ClusterState 是以該Node 的視角來看, 集群目前所處的狀態.
1.2 slot
- 以分片的方式來保存DB中的鍵值對, 分為16384個slot.
- 當所有16384個槽都有節點處理時,集群處理OK上線狀態.否則為fail.
- 記錄當前Node 被指派的槽信息:
clusterNode{ char slots; int numslots};
- 其中, slots 二進制位為1時代表處理相應數值的槽.
- 節點會在集群內相互傳播槽指派信息.
- 記錄著集群中所有槽的指派信息:
clusterState{ clusterNode * slots[16384]};
. - 通過查看state.slots[i] 的值, 即可得到負責處理槽i 的Node.
- 記錄著集群中所有槽的指派信息:
- 可以在online 狀態下進行重新分片動作.
1.3 在集群中執行命令
- 接收命令的Node 會計算出key 屬于那個槽,
- 當該槽并未被指派給自己時, 會向客戶端返回MOVED 錯誤, 指引客戶端redirect 至正確的Node.
- 計算key 所屬槽的:
slot_number(key) = CRC16(key) & 16383;
- 集群的客戶端會與多個Node創建?TCP 鏈接, redirect 命令通過轉換TCP 來完成命令的發送.
- 如果與想要redirect 的節點還未建立TCP 鏈接, 會先建立后再進行轉向.
- 集群模式下的redis-cli 客戶端會隱藏MOVED 錯誤而自動完成redirect.
- 而單機模式下的客戶端因為無法進行redirect, 會打印出MOVED錯誤.
- Node 只能使用0號數據庫, 而單機服務器并無該限制.
- 記錄槽和鍵的對應關系:
clusterState{ zskiplist *slot_to_keys}
.
- 記錄槽和鍵的對應關系:
1.4 ASK 錯誤
- 在重新分片期間, 可能會出現槽的鍵值同時分布在源和目的節點中的情況.
- 若源節點發現鍵已不在自身上, 則向客戶端返回ASK錯誤, 來指引客戶端轉向正在導入槽的目標節點.
1.5 復制與故障轉移.
- master 和slave
- master負責處理槽;
- slave ?會復制master 的數據, 并在master 下線后, 代替它繼續進行處理.
- 設置slave節點:
cluster replicate <node_id>
.- 接收者設置自己的
clusterNode *slaveof
屬性為node_id, 同時修改flags. - 此時會開始從master 進行數據復制的動作: slaveof <master_ip> <master_port>.
- 接收者設置自己的
-
clusterNode{ clusterNode *slaves, numslaves}
.- 記錄著正在復制該master的slave信息
- 故障檢測
- 定期發送PING 消息, 若規定時間內沒有返回, 則將節點標記為probable fail(PFAIL).
- 若集群中, 若半數以上處理槽的master 都將某節點設置為PFAIL, 則該節點將被標記為FAIL, 同時進行廣播.
- 故障轉移
- 如果slave 發現master 已下線, 則開始進行故障轉移.
- 選舉新的master
- 集群的配置紀元為自增計數器, 每一次故障轉移會進行自增.
- 每個處理槽的master 都有一票, 第一個向master 要求投票的slave 將獲得master 的投票.
- 收集到大于N/2個投票的slave 會成為新的 master.
1.6 消息
- PING消息.
- 默認每秒發送一次.
- 過程: 從已知節點列表中隨機選出五個節點, 對其中的最長時間沒有發送過PING 的節點發送該消息, 來檢測其是否在線.
- PONG消息.
- 對MEET 和PING 的響應.
- 或者用于讓其它節點刷新對自己狀態的認識
- 如: 故障轉移后新的master.
- PUBLISH消息.
- 節點收到后, 會執行該命令, 然后向集群廣播PUBLISH 消息.
2. 復制
復制分為: 初次復制和斷線后復制.
2.1 舊版的實現
- 同步的實現.
- master 在收到SYNC 命令后, 從后臺生成RDB 文件, 并使用緩存記錄從現在開始的所有寫操作.
- slave 接收并載入RDB 文件, 并接收執行緩存中記錄的寫操作.
- 命令傳播.
- 在同步過后, master 會將其執行的更新命令發送給master, 說明其已達到一致狀態.
- 由于執行SYNC 命令非常耗費資源, 所以在斷線后復制時效率不佳.
2.2 新版的實現
- 完整重同步.
- 用于初次復制, 步驟等于SYNC.
- 部分重同步.
- 處理斷線后重復制.
- master 只用將斷線期間的更新命令發送給slave, 并讓其執行.
- master 和slave 各自維護各自的replication offset, 并分別在傳播和接收傳播時加上N.
- 通過對比offset 的值來得知主從是否處于一致狀態.
- master 和slave 各自維護各自的replication offset, 并分別在傳播和接收傳播時加上N.
- 復制積壓緩存區.
- 由master 負責維護的fixed-size 的FIFO 隊列, 默認大小為1MB.
- 進行命令傳播時, 將命令寫入緩存區.
- 緩存區同時會為隊列中每個字節記錄相應的offset.
- 對于斷線重連的slave, 若其offset 之后的數據仍然存在于緩存區, 則使用部分重同步, 否則就需要使用完全重同步.
- 根據slave 斷線后重連的時長和master 的寫命令頻率來調整緩存區大小.
- 服務器運行ID.
- 對于斷線重連的slave, 會將保存的master 的ID發送給重新連接到的master, 若相同,則可以嘗試進行部分重同步.
2.3 心跳檢測
replconf ACK <offset>.
- 用于檢測主從服務器的網絡連接狀態.
- 輔助實現min-slaves 配置選項, 以防止master 在不安全狀態下執行寫命令.
- 檢測命令丟失.
- 當master 發現slave 的offset 小于自己的offset, 會從復制積壓緩存區中找出slave 缺失的數據并進行重發.
3. Sentinel
- 本質上是運行在特殊模式下的Redis 服務器.
- sentinel 成為master 的客戶端, 并創建兩個指向master 的異步網絡連接.
- 命令連接. 用于向master 發送命令, 并接收命令回復.
- 訂閱連接. 訂閱master 的sentinel:hello channel.
- 獲取master 信息.
- 每隔10秒通過命令連接向master 發送INFO 命令, 然后通過分析返回?獲取master 的狀態.