redis 哨兵和 叢集
哨兵模式
哨兵模式是redis高可用
的實現方式之一
使用一個或者多個哨兵(Sentinel)例項組成的系統,對redis節點進行監控,在主節點出現故障的情況下,能將從節點中的一個升級為主節點,進行故障轉義,保證系統的可用性。
哨兵們是怎麼感知整個系統中的所有節點(主節點/從節點/哨兵節點)的
- 首先主節點的資訊是配置在哨兵(Sentinel)的配置檔案中
- 哨兵節點會和配置的主節點建立起兩條連線
命令連線
和訂閱連線
- 哨兵會通過
命令連線
每10s傳送一次INFO
命令,通過INFO命令
,主節點會返回自己的run_id和自己的從節點資訊
- 哨兵會對這些從節點也建立兩條連線
命令連線
和訂閱連線
- 哨兵通過
命令連線
向從節點發送INFO
a. run_id
b. role
c. 從伺服器的複製偏移量 offset
d. 等 - 因為哨兵對與叢集中的其他節點(主從節點)當前都有兩條連線,
命令連線
和訂閱連線
a. 通過命令連線
向伺服器的_sentinel:hello
頻道傳送一條訊息,內容包括自己的ip埠、run_id、配置紀元(後續投票的時候會用到)等
b. 通過訂閱連線
對伺服器的_sentinel:hello
頻道做了監聽,所以所有的向該頻道傳送的哨兵的訊息都能被接受到
c. 解析監聽到的訊息,進行分析提取,就可以知道還有那些別的哨兵服務節點也在監聽這些主從節點了,更新結構體將這些哨兵節點記錄下來
d. 向觀察到的其他的哨兵節點建立命令連線
訂閱連線
哨兵模式下的故障遷移
主觀下線
哨兵(Sentinel)節點會每秒一次的頻率向建立了命令連線的例項傳送PING命令,如果在down-after-milliseconds
毫秒內沒有做出有效響應包括(PONG/LOADING/MASTERDOWN)以外的響應,哨兵就會將該例項在本結構體中的狀態標記為SRI_S_DOWN
主觀下線
客觀下線
當一個哨兵節點發現主節點處於主觀下線狀態是,會向其他的哨兵節點發出詢問,該節點是不是已經主觀下線了。如果超過配置引數quorum
個節點認為是主觀下線時,該哨兵節點就會將自己維護的結構體中該主節點標記為SRI_O_DOWN
客觀下線
詢問命令SENTINEL is-master-down-by-addr <ip> <port> <current_epoch> <run_id>
引數 | 意義 |
---|---|
ip/port | 當前認為下線的主節點的ip和埠 |
current_epoch | 配置紀元 |
run_id | *標識僅用於詢問是否下線 有值標識該哨兵節點希望對方將自己設定為leader 詢問時用*,選舉時用run_id |
leader選舉
在認為主節點客觀下線
的情況下,哨兵節點節點間會發起一次選舉,命令還是上面的命令SENTINEL is-master-down-by-addr <ip> <port> <current_epoch> <run_id>
,只是run_id
這次會將自己的run_id
帶進去,希望接受者將自己設定為主節點。如果超過半數以上的節點返回將該節點標記為leader的情況下,會有該leader對故障進行遷移
故障遷移
- 在從節點中挑選出新的主節點
a. 通訊正常
b. 優先順序排序
c. 優先順序相同是選擇offset最大的 - 將該節點設定成新的主節點
SLAVEOF no one
,並確保在後續的INGO命令時,該節點返回狀態為master - 將其他的從節點設定成從新的主節點複製,
SLAVEOF命令
- 將舊的主節點變成新的主節點的從節點
優缺點
- 優點
高可用,在主節點故障時能實現故障的轉移 - 缺點:好像沒辦法做到水平拓展,如果內容很大的情況下
叢集模式
官方提供的分散式方案(槽指派/重新分片/故障轉移)
叢集內的節點,都會有個資料結構儲存整個叢集內的節點資訊
//整體
struct clusterState{
clusterNode *mySelf;
....
dict *nodes; //叢集內的所有節點
}
// 單個節點
struct clusterNode {
char name[];
char ip[];
int port;
clusterLink *link; //儲存節點間,連線的資訊
int flags; //狀態標記
}
//節點間連線的資訊
struct clusterLink{
mstime_t ctime; //建立時間
int fd; //tcp套接字描述符
sds sndbuf; // 輸出快取區
sds rcvbuf; //輸入快取區
struct clusterNode *node;
}
槽指派
redis叢集可以被分為16384個槽,只有這些槽全被指派了處理的節點的情況下,叢集的狀態才能是上線狀態(ok)
操作redis叢集的時候,將key作為引數,就可以計算出對應的處理槽上,所以儲存等操作都應該在該槽對應的節點上。通過這種方式,可以完美的實現叢集儲存的水平拓展。
def slot_number(key):
return CRC16(key) & 16383
//得到的結果就是槽的序號
槽指派的資訊是怎麼儲存的
struct clusterState{
clusterNode *slots[16384]
}
struct clusterNode{
unsigned char slots[16384/8]
}
通過上面兩個結構體中的定義可以看出,槽指派的資訊是分了兩種方式,儲存在結構體裡面。
分兩種儲存的好處
1. 如果需要判斷某一個節點負責的槽,只需要獲取方式二中的陣列做判斷就可以
2.如果找某個槽是哪個節點負責,只需要獲取方式一的列表,一查就知道
重新分片
將已經指派給節點的槽,重新執行新的節點。
故障轉移
發現故障節點
- 叢集內的節點會向其他節點發送PING命令,檢查是否線上
- 如果未能在規定時間內做出PONG響應,則會把對應的節點標記為疑似下線
- 叢集中一半以上
負責處理槽的主節點
都將主節點X標記為疑似下線的話,那麼這個主節點X就會被認為是已下線
- 向叢集廣播主節點X
已下線
,大家收到訊息後都會把自己維護的結構體裡的主節點X標記為已下線
從節點選舉
- 當從節點發現自己複製的主節點已下線了,會向叢集裡面廣播一條訊息,要求所有有投票權的節點給自己投票(
所有負責處理槽的主節點都有投票權
) - 主節點會向第一個給他發選舉訊息的從節點回復支援
- 當支援數量超過N/2+1的情況下,該從節點當選新的主節點
故障的遷移
- 新當選的從節點執行
SLAVEOF no one
,修改成主節點 - 新的主節點會撤銷所有已下線的老的主節點的槽指派,指派給自己
- 新的主節點向叢集傳送命令,通知其他節點自己已經變成主節點了,負責哪些槽指派
- 新的主節點開始處理自己負責的槽的命令
叢集模式和哨兵模式的區別
- 哨兵模式監控權交給了哨兵系統,叢集模式中是工作節點自己做監控
- 哨兵模式發起選舉是選舉一個leader哨兵節點來處理故障轉移,叢集模式是在從節點中選舉一個新的主節點,來處理故障的轉移