redis集羣是redis提供分佈式數據庫方案,
集羣經過分片(Sharding)來進行數據共享,並提供複製和故障轉移功能。
節點
redis集羣一般由多個節點(node)組成,在開始每一個node 都是相互獨立的。
要組建成真正可工做的集羣,咱們必須將各個獨立的節點鏈接起來,構成一個包含多個節點的集羣。
命令
cluster meet <ip> <port>
向一個node 發送命令 cluster meet,讓節點與ip/port所指定的節點 進行握手(handshake),
當握手成功時,node節點就會將ip/port所指定的節點添加到node節點當前所在的集羣中。
舉例:
三個節點
- 127.0.0.1:7000
- 127.0.0.1:7001
- 127.0.0.1:7002
啓動節點
一個節點就是一個運行在集羣模式下的redis服務器,redis會在啓動時 根據 cluster-enabled 配置選項
肯定是否來開啓集羣模式。
運行在集羣模式的redis服務器,會繼續使用全部在單機模式中使用的服務器組件。
cluster meet 命令的實現
經過向節點A發送Cluster MEET 命令,客戶端可讓接收命令的節點A將另外一個節點B添加到節點A當前所在的集羣裏面:
很像TCP/IP 三次握手
槽指派
redis集羣經過分片的方式來保存數據庫中的鍵值對:
- 集羣的整個數據庫被分爲16384個槽(slot)
- 數據庫中的每一個鍵都屬於這16384個槽的其中一個
- 集羣中的每一個節點能夠處理0或最多16384個槽
當數據庫中的16384個槽都有節點在處理時,集羣處於上線狀態(ok),
若是數據庫中有任何一個槽沒有獲得處理,那麼集羣處於下線狀態(fail)。
上面使用cluster meet 命令將7000 7001 7002 三個節點鏈接到同一個集羣裏,
不過這個集羣目前仍然處於下線狀態,由於集羣中的三個節點都沒有處理任何槽
命令:cluster info
命令:cluster addslots
添加一個或多個槽指派(assign)給某節點 來負責
將槽0--槽5000 指派給節點7000 負責
傳播節點的槽指派信息
一個節點除了記錄本身需處理的槽記錄,還會將本身的槽信息,發送給集羣中的其餘節點;
告訴其餘節點,本身所負責的哪些模塊。
每一個節點都會通告本身的槽記錄給其餘任何節點,而每一個節點也會記錄本身的槽記錄;
所以,集羣中的每一個節點都會知道數據庫中的16384個槽分別被指派給了集羣中的哪些節點。
在集羣中執行命令
在對數據庫中的16384個槽都進行了指派以後,集羣就會進入上線狀態,
這時客戶端就能夠向集羣中的節點發送數據命令。
當客戶端向節點發送與數據庫鍵有關的命令時,接收命令的節點會計算出命令要處理的數據庫鍵屬於哪一個槽
並檢查這個槽是否指派給了本身:
- 若是鍵所在的槽正好指派了當前節點,
- 若是鍵所在的槽並無指派給當前節點,
- 那麼節點會向客戶端返回一個moved 錯誤,
- 指引客戶端轉向至正確的節點,並再次發送以前想要執行的命令。
流程圖
計算鍵屬於哪一個槽
def slot_number(key):
return CRC16(key) & 16383
crc16 用於計算鍵KEY的crc-16校驗和,而 & 16383 則用於計算出一個介於0至 16383 之間的整數做爲KEY的槽號。
查看鍵的槽號
命令:cluster keyslot <key>
MOVED錯誤
moved 錯誤的格式爲:
MOVED <slot> <ip>:<port>
slot 爲鍵所在的槽,而ip/port則是負責處理槽slot的節點的IP地址和端口號。
- 之因此能指定具體的ip,端口號。是由於每一個節點都知曉每一個槽由哪一個節點負責
一個集羣客戶端一般會與集羣中的多個節點建立套接字鏈接,而所謂的節點轉向實際上就是換一個套接字來發送命令
若是客戶端還沒有與想要轉向的節點建立套接字鏈接,那麼客戶端會先根據MOVED錯誤提供的IP地址和端口號來鏈接節點,
而後再進行轉向。
從新分片
redis 集羣的從新分片操做能夠將任意數量已經指派給某個節點(源節點)的槽改成指派給另外一個節點(目標節點)
而且相關槽所屬的鍵值對 也會從源節點被移動到目標節點。
從新分片操做能夠在線(online)進行,在從新分片的過程當中,集羣不須要下線,而且源節點和目標節點均可以繼續處理命令請求。
從新分片的實現原理
從新分片由redis 集羣管理軟件 redis-trib 負責執行。
redis-trib 對集羣的單個槽進行從新分片的步驟以下:
- redis-trib 對目標節點發送 cluster setslot <slot> importing <sorce_id> 命令
- 讓目標節點準備好從源節點導入(import)屬於槽slot的鍵值對
- redis-trib 對源節點發送 cluster setslot <slot> migrating <target_id>命令
- 讓源節點準備好將屬於槽slot 的鍵值對槽slot的鍵值對遷移(migrate)
- redis-trib 向源節點發送 cluster getkeysinslot <slot> <count>命令
- 獲取最多count 個 屬於槽slot的鍵值對的鍵名(key name)
- 對於步驟3獲取的每一個鍵名,redis-trib都向源節點 發送 migrate <target_ip> <tartget_port> <key_name> 0 <timeout> 命令。
- 重複執行步驟3和步驟4,直到源節點保存的全部屬於槽slot 的鍵值對都被遷移到目標節點爲止。
- redis-trib 向集羣中的任意一個節點發送 cluster setslot <slot> node <target_id> 命令,
- 將槽slot指派給目標節點,這一信息經過消息發送至整個集羣
- 最終集羣中的全部節點都會知道槽slot 已經指派給了目標節點。
整個過程:
ASK 錯誤
當客戶端向源節點發送一個域數據庫鍵有關的命令,
而且命令要處理的數據庫鍵值對剛好就屬於正在被遷移的槽時:
- 源節點會先在本身的數據庫裏查找指定的鍵,若是找到的話,就直接執行客戶端發送的命令
- 若是源節點沒有找到指定的鍵,那麼這個鍵有可能已經被遷移到了目標節點
-
- 源節點將向客戶端返回一個ASK錯誤
-
- 指引客戶端轉向正在導入槽的目標節點,並再次發送以前想要執行的命令。
過程
例子:
槽16198 中的love 鍵 從7002 遷移到7003
ASKING標誌位
ASKING命令惟一要作的就是打開發送該命令的客戶端的REDIS_ASKING標識。
通常狀況下:
- 如客戶端向節點發送一個關於槽i的命令,而槽i又沒有指派給這個節點,
-
- 若是顯示節點正在導入槽i,而且發送命令的客戶端帶有REDIS_ASKING標識,
-
- 那麼節點將破例執行這個關於槽i的命令一次
-
- 雖然槽i 尚未正式分配給節點,但只要有ASKING標識也就破例執行一次。
判斷過程
注意:客戶端的REDIS_ASKING標識是一個一次性標識;
例子:
若是咱們在成功執行GET命令以後,再次向節點7003發送GET命令,
那麼第二次發送的GET沒拿過來將執行失敗,由於這時客戶端的REDIS_ASKING標識已經被移除:
ASK錯誤和MOVED錯誤的區別
- MOVED錯誤表明槽的負責權已經從一個節點轉移到了另外一個節點
-
- 在客戶端收到關於槽i 的moved 錯誤以後,
-
- 客戶端每次遇到關於槽i的命令請求時,均可以直接將命令請求發送至moved錯誤所指向的節點。
- 而ASK錯誤只是兩個節點在遷移槽的過程當中使用的一種臨時措施:
-
- 客戶端只是臨時將關於槽i的命令請求,發送至ASK 指引的 節點;
- 下次發送仍是發送到當前 節點,直到再收到ASK 錯誤,而後再指引到 新節點。
複製與故障轉移
Redis集羣中的節點爲主節點和從節點,其中主節點用於處理槽,而從節點則用於複製某個主節點,
並在被複制的主節點下線時,代替下線主節點繼續處理命令請求。
例子:
若是這時候,節點7000 進入下線狀態,那麼集羣中仍在正常運做的幾個主節點將在節點7000 的兩個從節點
從7004 和7005 中選出一個節點做爲新的主節點,這個新的主節點將接管原來節點7000 負責處理的槽,並
繼續處理客戶端發送的命令請求。
設置從節點
發送命令:
CLUSTER REPLICATE <node_id>
可讓接收命令的節點成爲node_id 所指定節點的從節點,並開始對主節點進行復制:
故障檢測
集羣中的每一個節點都會按期地向集羣中的其餘節點發送ping消息,以此來檢測對方是否在線,
若是接收ping消息的節點每頁在規定的時間內,發送ping消息的節點回復,那麼源節點則將目標節點標記爲疑似下線(probale fail,PFAIL)
集羣中的各個節點會經過相互發送消息的方式來交換集羣中各個節點的狀態信息。
若果一個集羣裏面,半數以上的主節點 都將某個主節點 報告爲疑似下線,那麼這個主節點將被標記爲已下線(FAIL).
將主節點標記爲已下線的節點會向集羣廣播一條關於該主節點的FAIL消息,全部收到這條FAIL消息的節點都會當即將
該主節點標記爲已下線。
故障轉移
當一個從節點發現本身正在複製的主節點進入了已下線狀態時,
從節點將開始對下線主節點進行故障轉移,故障轉移步驟:
- 下線主節點的全部從節點裏面,會有一個被選中做爲新的主
- 被選中的從節點會執行SLAVEOF no one ,成爲 新的主
- 新主節點,處理原主的全部槽
- 新主節點向集羣廣播一條pong消息,讓集羣中的其餘節點當即知道此節點已經變成主節點
- 新主節點開始接受和本身負責處理的槽有關的命令請求,故障轉移完成。
選舉新的主節點
集羣選舉新的主節點的方法:
- 集羣的配置紀元是一個自增計數器,它的初始值爲0
- 當集羣裏的某個節點開始一次故障轉移操做時,集羣配置紀元的值會增一
- 對於每一個配置紀元,集羣裏每一個負責處理槽的主節點都有一次投票的機會,
- 而第一個向主節點要求投票的從節點將得到主節點的投票
- 當從節點發現本身的主節點下線,
- 從節點向集羣廣播一條消息,要求收到消息的主節點,投票給本身。
-
- CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST消息
- 若是主節點有投票權(它正在處理槽),而且還沒有投票。
- 將向要求投票的從庫,返回一條信息
-
- CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK
- 表示這個主節點支持該從節點成爲新的主節點
- 從節點經過接收消息的個數,統計本身得到了多少主節點的支持
- CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK
- 如集羣中有N個具備投票權的主節點,那麼從節點收集到大於等於N/2+1張支持票時,從節點當選爲新的主節點。
- 由於在每一個配置紀元裏,主節點只能投一次票,因此獲得N/2+1的從節點只會有一個。
- 若是在一個配置紀元裏沒有從節點收集到足夠多的支持票,那麼集羣進入一個新的配置紀元,並再次進行選舉,直到選出新的主節點爲止
注意:選舉新主的方法與sentinel的方法很是相似,都是基於Raft算法的領頭選舉方法來實現的。
消息
集羣中的各節點經過發送和接收消息來進行通訊。
節點發送的消息主要有如下五種:
- MEET消息
-
- 當發送者接到客戶端發送的CLUSTER MEET命令時
- 發送者會向接收者發送MEET消息,請求接收者加入到發送者當前所處的集羣裏面。
- PING消息
-
- 集羣裏面每一個節點默認每秒會從已知節點列表隨機選出5個節點,
- 對這5個節點中最長時間沒有發送過PING消息的節點發送PING消息,以此來檢測被選中的節點是否在線
- 此外,PONG消息會對PING消息產生影響
-
- 如節點A最後一次收到節點B發送的PONG消息的時間,
- 距離當前時間超過了節點A的cluser-node-timeout的一半
- 則A 向B 發送PING 消息。
- 防止節點A 長時間沒有隨機選中B,致使節點B的信息更新滯後
- PONG消息
-
- 當接受者收到MEET消息或者PING消息,回一條PONG消息以確認收到這條消息。
- 此外,一個節點也能夠經過向集羣廣播本身的PONG消息來讓集羣中的其餘節點當即刷新關於自身的認識。
-
- 如,故障轉移操做成功以後,新的節點會向集羣廣播一條PONG消息,
- 以此來讓集羣中的其餘節點當即知道這個節點已經變成了主節點。
- FAIL消息
-
- 當主節點A判斷主節點B已經進入FAIL狀態時,
- 節點A向集羣廣播一條關於B的FAIL消息,
- 全部收到這條消息的節點當即向B 節點標記爲已下線
- PUBLISH消息
-
- 當節點接收到一個PUBLISH命令時,節點會執行這個命令,
- 並向集羣廣播一條PUBLISH消息,
- 全部接收到這條PUBLISH消息的節點都會執行相同的PUBLISH命令。
例子:
發送PING 消息和返回PONG消息的例子,假設在一個包含A,B,C,D,E,F六個節點的集羣裏:
- 節點A向節點D發送PING消息,而且消息裏面包含了節點B和節點C的信息
-
- 當節點D收到這個PING消息,它將更新本身對節點B和節點C的認識
- 以後,節點D將向節點A放回一條PONG消息,而且消息裏面包含了節點E和節點F的消息,
-
- 當節點A收到這條PONG消息時,它將更新本身對節點E和節點F的認識。
整個過程
FAIL消息實現的過程
PUBLISH消息實現過程
總結:
- 節點經過握手來將其餘節點添加到本身所處的集羣當中。
- 集羣中的16384個槽能夠分別指派給集羣中的各個節點,
-
- 每一個節點都會記錄哪些槽指派給了本身
- 而哪些槽又被指派給了其餘節點
- 節點在接到一個命令請求時,會先檢查這個命令請求要處理的鍵所在的槽是否本身負責
-
- 若是不是,節點將向客戶端返回MOVED錯誤,
- MOVED錯誤攜帶的信息,能夠指引客戶端轉向正確的節點
- 對Redis集羣的從新分片工做是由redis-trib負責執行的。
-
- 從新分片的關鍵是將屬於某個槽的全部鍵值對從一個節點轉移到另外一個節點
- 若是節點A正在遷移槽i至B節點,
-
- 那麼當節點A沒能在本身的數據庫中找到命令指定的數據庫鍵時,
-
- 節點A會向客戶端返回一個ASK錯誤,
- 指引客戶端到節點B繼續查找指定的數據庫鍵
- MOVED錯誤表示槽的負責權已經從一個節點轉移到了另外一個節點,
-
- ASK錯誤只是兩個節點在遷移槽的過程當中使用的一種臨時措施
- 集羣裏的從節點用於複製主節點,並在主節點下線時,代替主節點繼續處理命令請求
- 集羣中的節點經過發送和接收消息來進行通行,常見消息類型:
-
- MEET,PING,PONG,PUBLISH,FAIL