redis 集群


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 對集群的單個槽進行重新分片的步驟如下:
  1. redis-trib 對目標節點發送 cluster setslot <slot> importing <sorce_id> 命令
    • 讓目標節點准備好從源節點導入(import)屬於槽slot的鍵值對
  2. redis-trib 對源節點發送 cluster setslot <slot> migrating <target_id>命令
    • 讓源節點准備好將屬於槽slot 的鍵值對槽slot的鍵值對遷移(migrate)
  3. redis-trib 向源節點發送 cluster getkeysinslot <slot> <count>命令
    • 獲取最多count 個 屬於槽slot的鍵值對的鍵名(key name)
  4. 對於步驟3獲取的每個鍵名,redis-trib都向源節點 發送 migrate <target_ip> <tartget_port> <key_name> 0 <timeout> 命令。
    • 被選中的鍵,從源節點遷移至目標節點
  5. 重復執行步驟3和步驟4,直到源節點保存的所有屬於槽slot 的鍵值對都被遷移到目標節點為止。
    • 每次遷移如下圖所示
  6. redis-trib 向集群中的任意一個節點發送 cluster setslot <slot> node <target_id> 命令,
    • 將槽slot指派給目標節點,這一信息通過消息發送至整個集群
    • 最終集群中的所有節點都會知道槽slot 已經指派給了目標節點。

 

 
 
整個過程:
 

 

 
ASK 錯誤

 
當客戶端向源節點發送一個域數據庫鍵有關的命令,
並且命令要處理的數據庫鍵值對恰好就屬於正在被遷移的槽時:
  • 源節點會先在自己的數據庫里查找指定的鍵,如果找到的話,就直接執行客戶端發送的命令
  • 如果源節點沒有找到指定的鍵,那么這個鍵有可能已經被遷移到了目標節點
    • 源節點將向客戶端返回一個ASK錯誤
      • 指引客戶端轉向正在導入槽的目標節點,並再次發送之前想要執行的命令。
 
過程

 

 
例子:
槽16198 中的love 鍵 從7002 遷移到7003
 

 

 
ASKING標志位
 
ASKING命令唯一要做的就是打開發送該命令的客戶端的REDIS_ASKING標識。
 
一般情況下:
  • 如客戶端向節點發送一個關於槽i的命令,而槽i又沒有指派給這個節點,
    • 那么節點將向客戶端返回一個MOVED 錯誤;
  • 如果顯示節點正在導入槽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消息的節點都會立即將
該主節點標記為已下線。

 

 
故障轉移
當一個從節點發現自己正在復制的主節點進入了已下線狀態時,
從節點將開始對下線主節點進行故障轉移,故障轉移步驟:
  1. 下線主節點的所有從節點里面,會有一個被選中作為新的主
    • 被選中的從節點會執行SLAVEOF no one ,成為 新的主
  2. 新主節點,處理原主的所有槽
  3. 新主節點向集群廣播一條pong消息,讓集群中的其他節點立即知道此節點已經變成主節點
  4. 新主節點開始接受和自己負責處理的槽有關的命令請求,故障轉移完成。
 
選舉新的主節點
集群選舉新的主節點的方法:
  1. 集群的配置紀元是一個自增計數器,它的初始值為0
  2. 當集群里的某個節點開始一次故障轉移操作時,集群配置紀元的值會增一
  3. 對於每個配置紀元,集群里每個負責處理槽的主節點都有一次投票的機會,
    • 而第一個向主節點要求投票的從節點將獲得主節點的投票
  4. 當從節點發現自己的主節點下線,
    • 從節點向集群廣播一條消息,要求收到消息的主節點,投票給自己。
      • CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST消息
  5. 如果主節點有投票權(它正在處理槽),並且尚未投票。
    • 將向要求投票的從庫,返回一條信息
      • CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK
    • 表示這個主節點支持該從節點成為新的主節點
  6. 從節點通過接收消息的個數,統計自己獲得了多少主節點的支持
    • CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK
  7. 如集群中有N個具有投票權的主節點,那么從節點收集到大於等於N/2+1張支持票時,從節點當選為新的主節點。
  8. 因為在每個配置紀元里,主節點只能投一次票,所以得到N/2+1的從節點只會有一個。
  9. 如果在一個配置紀元里沒有從節點收集到足夠多的支持票,那么集群進入一個新的配置紀元,並再次進行選舉,直到選出新的主節點為止
 
注意:選舉新主的方法與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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM