Redis集群是Redis提供的分布式資料庫方案,集群通過分片(Sharding)來進行資料共享,并提供復制和故障轉移功能,
節點
一個節點就是一個運行在集群模式下的Redis服務器,Redis服務器在啟動的時候會根據cluster-enabled配置項來決定是否開啟服務器的集群模式,
- 節點當前狀態
typedef struct clusterNode {
// 創建節點時間
mstime_t ctime;
// 節點的名字,40個十六進制字符組成
char name[CLUSTER_NAMELEN];
// 節點標識
// 使用各種不同的標識值記錄節點的角色(比如:主節點、從節點)
// 以及節點目前所處的狀態(比如:在線或者下線)
int flags;
// 節點當前的配置紀元,用于實作故障轉移
uint64_t configEpoch;
// 記錄節點的槽指派資訊
unsigned char slots[CLUSTER_SLOTS/8];
// 記錄節點負責處理的槽的數量
int numslots;
// 主節點的slave節點數量
int numslaves;
// 從節點資訊
struct clusterNode **slaves;
// ……
// 節點的ip地址
char ip[NET_IP_STR_LEN];
// 節點的埠號
int port;
// 保存連接節點所需的有關資訊
clusterLink *link;
// ……
} clusterNode;
- 節點所需的有關資訊,可以理解為是
clusterNode的擴展資訊用clusterNode.link來存盤
typedef struct clusterLink {
// 連接的創建時間
mstime_t ctime;
// 輸出緩沖區
sds sndbuf;
// 輸入緩沖區
sds rcvbuf;
// 與這個連接相關聯的節點
struct clusterNode *node;
} clusterLink;
- 集群狀態資訊,記錄了在當前節點的視角下,集群目前所處的狀態
typedef struct clusterState {
// 指向當前節點的指標
clusterNode *myself;
// 集群當前的配置紀元,用于實作故障轉移
uint64_t currentEpoch;
// 集群當前的狀態:是在線還是下線
int state;
// 集群中至少處理著一個槽的節點的數量
int size;
// 集群節點名單(包括myself節點)
// 字典的鍵為節點的名字,字典的值為節點對應的clusterNode結構
dict *nodes;
// ……
} clusterState;
通過發送CLUSTER MEET命令,可以讓發送命令的節點A和接收命令的節點B彼此都添加到clusterNode.nodes中,然后節點A將節點B的資訊通過Gossip協議傳播給集群中的其他節點,讓其他節點也與節點B進行握手,最終,經過一段時間后,節點B會被集群中的所有節點認識,
槽指派
Redis集群通過分片的方式來保存資料庫中的鍵值對:集群的整個資料庫被分為16384個槽,集群中的每個節點可以處理0個或者最多16384個槽,所有的槽都有節點處理時,集群就屬于上線狀態,否則下線狀態,
在集群中執行的命令
當資料庫中的16384個槽都進行了指派后,集群就會進入上線狀態,客戶端就可以向集群中的節點發送資料命令了,具體步驟如下:
- 計算鍵屬于哪個槽
- 判斷槽是否由當前節點負責處理,如果
clusterNode.nodes[i]等于clusterNode.myself,那就說明i是由當前節點負責,節點可以執行客戶端發送的命令 - MOVED錯誤,當節點發現鍵所在的槽并非由自己負責處理的時候,節點就會向客戶端回傳一個MOVED錯誤(
MOVED <slot> <ip>:<port>),引導客戶端轉向至正在負責槽的節點
重新分片
Redis集群的重新分片操作可以將任意數量已經指派給某個節點(源節點)的槽改為指派給另一個節點(目標節點),并且相關槽所屬的鍵值對也會從源節點被移動到目標節點,
重新分片的步驟
Redis集群的重新分片操作是由Redis的集群管理軟體redis-trib負責執行的,具體步驟如下:
redis-trib對目標節點發送CLUSTER SETSLOT <slot> IMPORTING <source_id>命令,讓目標節點準備好從源節點匯入屬于槽slot的鍵值對redis-trib對源節點發送CLUSTER SETSLOT <slot> MIGRATING <target_id>命令,讓源節點準備好將屬于槽slot的鍵值對遷移至目標節點redis-trib向源節點發送CLUSTER GETKEYSINSLOT <slot> <count>命令,獲得最多count個屬于槽slot的鍵值對的鍵名- 對于步驟3獲得的每個鍵名,
redis-trib都向源節點發送一個MIGRATE <target_ip> <target_port> <key_name> 0 <timeout>命令,將被選中的鍵原子地從源節點遷移至目標節點 - 重復執行步驟3和步驟4,直到源節點保存的所有屬于槽slot的鍵值對都被遷移至目標節點為止
redis-trib向集群中的任意一個節點發送CLUSTER SETSLOT <slot> NODE <target_id>命令,將槽slot指派給目標節點,這一個指派資訊會通過訊息發送至整個集群,最終集群中的所有節點都會直到slot已經指派給了目標節點
ASK錯誤
當被遷移槽的一部分鍵值對保存在源節點里面,而另一部分鍵值對保存在目標節點里時,如果客戶端向源節點發送一個與資料庫鍵有關的命令,且要處理的鍵正好在遷移的槽時:
- 源節點會先在自己的資料庫里查找指定的鍵,如果找到就回傳
- 如果源節點中沒能找到,那么這個鍵可能已經被遷移到目標節點,源節點將向客戶端回傳一個ASK錯誤,指引客戶端轉向正在匯入槽的目標節點
- 當客戶端接收到ASK錯誤并轉向正在匯入槽的節點時,客戶端會先向節點發送一個ASKING命令,然后才重新發送想要執行的命令,這是因為客戶端如果不發送ASKING命令,而直接發送想要執行的命令的話,那么客戶端發送的命令將被節點拒絕執行,回傳MOVED錯誤
ASK錯誤和MOVED錯誤的區別
ASK錯誤和MOVED錯誤都會導致客戶端轉向,它們的區別在于:
- MOVED錯誤代表槽的負責權已經從一個節點轉移到了另一個節點
- ASK錯誤只是兩個節點在遷移槽的程序中使用的一種臨時措施
復制和故障轉移
Redis集群中的節點分為主節點(master)和從節點(slave),其中master用于處理槽,而slave用于復制某個master,并在被復制的master下線時,代替下線master繼續處理命令請求
設定從節點
向一個節點發送CLUSTER REPLICATE <node_id>命令,可以讓接收命令的節點成為node_id所指定節點的從節點,并開始對主節點進行復制
故障檢測
集群中每個節點都會定期地向集群中的其他節點發送PING訊息,以此來檢測對方是否在線,
- 如果接收PING訊息的節點沒有在規定的時間內,向發送PING訊息的節點回傳PONG訊息,那么發送PING訊息的節點就會將接收PING訊息的節點標記為疑似下線
- 如果一個集群里面,半數以上負責處理槽的主節點都將某個主節點A報告為疑似下線,那么這個主節點A將被標記為已下線,并向集群廣播一個關于主節點A已下線的訊息,所有收到訊息的主節點都會立即將主節點A標記為已下線
故障轉移
當一個節點發現自己正在復制的主節點已下線時,從節點將開始對下線主節點進行故障轉移,步驟如下:
- 復制下線主節點的所有從節點里面,會有一個從節點被選中
- 被選中的從節點執行
SLAVEOF no one命令,成為新的主節點 - 新的主節點會撤銷所有已下線主節點的槽指派,并將這些槽指派全部指向自己
- 新的主節點向集群廣播一條PONE訊息,通知所有其他節點自己已經成功接管了已下線節點負責處理的槽
- 新的主節點開始接收和自己負責處理的槽有關的命令請求,故障轉移完成
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/145072.html
標籤:Java
下一篇:IO模型
