《Redis开发与运维》学习第十章
第十章 Redis Cluster集群
什么叫集群??
集群(cluster)技術是一種較新的技術,通過集群技術,可以在付出較低成本的情況下獲得在性能、可靠性、靈活性方面的相對較高的收益,其任務調度則是集群系統(tǒng)中的核心技術。
集群是一組相互獨立的、通過高速網絡互聯(lián)的計算機,它們構成了一個組,并以單一系統(tǒng)的模式加以管理。一個客戶與集群相互作用時,集群像是一個獨立的服務器。集群配置是用于提高可用性和可縮放性。--------百度百科
Redis Cluster是把這種集群技術運用到Redis數(shù)據(jù)庫中。
這一章主要講解Redis Cluster的數(shù)據(jù)分布、節(jié)點通信、集群伸縮、請求路由、故障轉移、集群運維等幾個方面來介紹。
10.1 數(shù)據(jù)分布
10.1.1 數(shù)據(jù)分布理論
分布式數(shù)據(jù)庫首先要解決的是如何把整個數(shù)據(jù)集按照分區(qū)規(guī)則映射到多個節(jié)點的問題。即把數(shù)據(jù)集劃分到多個節(jié)點上,每個節(jié)點負責整體數(shù)據(jù)的一個子集。
這里需要重點關注的是數(shù)據(jù)分區(qū)規(guī)則。
常見的數(shù)據(jù)分區(qū)規(guī)則有哈希分區(qū)和順序分區(qū),如下表所示:
Redis Cluster采用的是哈希分區(qū)規(guī)則,接下來介紹常見的哈希分區(qū)規(guī)則;
1 節(jié)點取余規(guī)則
實現(xiàn)方案是:使用特定的數(shù)據(jù),如Redis的鍵或用戶ID,再根據(jù)節(jié)點數(shù)量N使用公式:hash(key)%N計算出哈希值,用來決定映射到哪一個節(jié)點上。這方案實現(xiàn)起來比較簡單,但是它存在一個問題:當節(jié)點數(shù)量變化時,數(shù)據(jù)節(jié)點映射關系需要重新計算,這樣會導致數(shù)據(jù)的重新紅轉移。
2 一致性哈希分區(qū)
實現(xiàn)思路:為系統(tǒng)中的每一個節(jié)點分配一個token,范圍在0~2的32次方,這些token構成一個哈希環(huán)。數(shù)據(jù)讀寫執(zhí)行節(jié)點查找操作時,先根據(jù)key計算hash值,然后順時針找到第一個大于等于該哈希值的token節(jié)點,這種方式最帶的優(yōu)點是插入刪除節(jié)點時只影響哈希環(huán)中相鄰的節(jié)點 。但是這種仍然存在很多問題。因此引入了虛擬槽技術對這種方式進行改進。
如下圖所示:
3 虛擬槽技術(Redis Cluster采用的集群分區(qū)方案)
虛擬槽分區(qū)巧妙的使用了哈希空間 ,使用分散度較好的哈希函數(shù)把所有的數(shù)據(jù)映射到一個固定范圍的整數(shù)集合中,整數(shù)定義為槽(slot).
這個范圍一般遠大于節(jié)點數(shù)目,比如Redis Cluster的槽范圍是0~16383.
槽是集群內數(shù)據(jù)管理和遷移的單位。采用大范圍的槽主要是為了方便數(shù)據(jù)拆分和集群擴展。
10.1.2 數(shù)據(jù)分區(qū)
Redis Cluster采用虛擬槽技術分區(qū),所有的鍵根據(jù)哈希函數(shù)映射到0~16383整數(shù)槽內,計算公式:slot=CRC16(key)&16383。每一個節(jié)點負責維護一部分槽以及槽所映射的鍵值數(shù)據(jù)。
如圖示:
10.2 搭建集群
三個步驟:
1)準備節(jié)點
2)節(jié)點握手
3) 分配槽
10.2.1 準備節(jié)點
Redis集群一般有多個節(jié)點組成,節(jié)點數(shù)量至少為6個才能保證組成完整高可用的集群。
每個節(jié)點需要開啟配置cluster-enabled yes.,讓redis運行在集群模式下。集群相關配置如下:
節(jié)點啟動過程如下:
10.2.2 節(jié)點握手
節(jié)點握手是指一批運行在集群模式下的節(jié)點通過Gossip協(xié)議彼此通信,達到感知對方的過程。
圖示:
命令cluster meet 127.0.0.1 6380讓節(jié)點6379和6380節(jié)點進行握手通信。cluster meet 是一個異步命令,執(zhí)行完后立刻返回。
6379和6380兩個節(jié)點通過meet命令建立通信后的集群結構如圖10-9.
工作流程如下:
對節(jié)點6379和6380執(zhí)行cluster nodes命令,可以看到他們已經感受道對方的存在:
我們只需在集群內任意節(jié)點上執(zhí)行cluster meet命令加入新節(jié)點,握手狀態(tài)會通過消息在集群內傳播,這樣其他節(jié)點會自動發(fā)現(xiàn)新節(jié)點并發(fā)起握手流程。最后執(zhí)行cluster nodes命令確認6個節(jié)點都彼此感知并組成集群:
通過cluster info命令可以獲取集群當前狀態(tài):
從輸出內容上看出:
被分配的槽cluster_slots_assigned是0 ,因為目前所有的槽沒有分配到節(jié)點,因此集群無法發(fā)完成槽到節(jié)點的映射。只有當16384個槽全部分配給節(jié)點后,集群才進入在線狀態(tài)。
10.2.3 分配槽
Redis集群把所有的數(shù)據(jù)映射到16384個槽中。
每個key會映射為一個固定的槽,只有當節(jié)點分配了槽,才會相應和這些槽相關的鍵命令。
通過cluster addslots為節(jié)點分配槽。
這里利用bash特性批量設置槽slots :
把16384個slot個槽平均分配給6379 6380 6381三個節(jié)點。
執(zhí)行cluster info查看集群狀態(tài):
作為一個完整的集群,每個負責處理槽的節(jié)點應該具有從節(jié)點,保證它出現(xiàn)故障時可以自動進行故障轉移。
集群模式下,Redis節(jié)點角色分為主節(jié)點和從節(jié)點。
首次啟動的節(jié)點和被分配的節(jié)點都是主節(jié)點,從節(jié)點負責復制主節(jié)點槽信息和相關的數(shù)據(jù)。
使用cluster replicate {nodeId}讓一個節(jié)點成為從節(jié)點。這個命令必須在對應的從節(jié)點上執(zhí)行,nodeId是要復制的主節(jié)點的節(jié)點ID :
復制完成后,整個集群結構如下:
如上,我們是依照Redis協(xié)議手動搭建一個集群。該集群由6個節(jié)點組成,3個主節(jié)點負責處理槽和相關數(shù)據(jù),3個從節(jié)點負責故障轉移。
手動集群比較繁瑣,Redis官方提供了redis-trib.rb工具方便我們快速搭建集群。
10.2.4 介紹用redis-trib.rb工具搭建集群
redis-trib.rb是一個采用Ruby實現(xiàn)的Redis集群管理工具。Ruby是一個依賴環(huán)境。
主要是介紹這個工具,不詳細展開啦,給出課本上的一些命令。
1 Ruby環(huán)境準備
2 準備節(jié)點
3 創(chuàng)建集群
4 集群完整性檢查
10.3 節(jié)點通信
10.3.1 通信流程
分布式存儲中需要提供維護節(jié)點元數(shù)據(jù)信息的機制。所謂元數(shù)據(jù)是指:節(jié)點負責哪些數(shù)據(jù),是否出現(xiàn)故障等狀態(tài)信心。
常見的元數(shù)據(jù)維護方式分為:集中式和P2P方式。 person to person 個人對個人
Redis集群采用P2P的Gossip(流言)協(xié)議:節(jié)點彼此之間不斷的通信交換信息,一段時間后所有的節(jié)點都會知道集群完整的信息,這種方式類似于流言傳播。原理如圖所示:
通信過程如下圖所示:
10.3.2 Gossip 消息
Gossip 消息的主要職責是信息交換。信息交換的載體就是節(jié)點彼此發(fā)送的Gossip消息。
常見的Gossip消息可分為:ping 消息 pong 消息 meet消息 fail消息 .他們的通信模式如下:
所有的消息格式為:消息頭和消息體。
消息頭包含發(fā)送節(jié)點自身狀態(tài)數(shù)據(jù),接收節(jié)點根據(jù)消息頭就可以獲取到發(fā)送節(jié)點的相關數(shù)據(jù)。結構如下:
10.3.3 節(jié)點選擇
Redis集群內節(jié)點通信采用固定頻率,因此節(jié)點每次選擇需要通信的節(jié)點列表很重要。
Redis內集群通信節(jié)點的選擇規(guī)則如下:
根據(jù)通信節(jié)點選擇的流程可以看出消息交換的成本主要體現(xiàn)在單位時間選擇發(fā)送消息的節(jié)點數(shù)量和每個消息攜帶的數(shù)據(jù)量。
1 選擇發(fā)送消息的節(jié)點數(shù)量
2 消息數(shù)據(jù)量
10.4 集群伸縮
10.4.1 伸縮原理
一般Redis集群可以實現(xiàn)對節(jié)點的靈活上線下線:其中原理可以抽象為槽和對應數(shù)據(jù)在不同節(jié)點之間靈活移動。
首先 看下之間搭建的集群槽和數(shù)據(jù)節(jié)點的對應關系:
其中三個主節(jié)點分別維護自己負責的槽和對應的數(shù)據(jù),如果希望加入一個節(jié)點實現(xiàn)集群擴容時,需要通過相關命令把一部分槽和數(shù)據(jù)遷移給新節(jié)點,如圖所示:
圖中每個節(jié)點把一部分槽和數(shù)據(jù)遷移到新的節(jié)點6385,每個節(jié)點負責的槽和數(shù)據(jù)相比之前少了從而達到集群擴容的目的。
可以簡單的理解為:
集群伸縮=槽和數(shù)據(jù)在節(jié)點之間的移動
10.4.2 擴容集群
擴容是分布式存儲最常見的需求。Redis集群擴容操作可以分為如下幾個步驟:
1) 準備新節(jié)點
2)加入集群
3)遷移槽和數(shù)據(jù)
1 準備新節(jié)點
新節(jié)點建議與集群內的節(jié)點配置保持一致
準備號配置后啟動兩個節(jié)點的命令如下:
2 加入集群
新節(jié)點依然采用cluster meet命令加入到現(xiàn)有集群中。在集群內任意節(jié)點處執(zhí)行cluster meet讓6385和6386兩個節(jié)點加入 :
新節(jié)點加入集群前后的示意圖:
新節(jié)點剛開始是主節(jié)點狀態(tài),但是由于沒有負責的槽,所以不能接受任何讀寫操作,對于新加入的節(jié)點的 后續(xù)操作一般有兩種::
1 為他遷移槽和數(shù)據(jù)實現(xiàn)擴容
2 作為其他主節(jié)點的從節(jié)點負責故障轉移
注意:
正式環(huán)境下建議使用redis-trib.rb add-node 命令加入新節(jié)點。一般不選擇手動添加的方式,而是利用redis-trib這個工具。
3 遷移槽和數(shù)據(jù)
加入集群后需要為新節(jié)點遷移槽和相關相關數(shù)據(jù)。
遷移過程是集群擴容中的最核心環(huán)節(jié)。
1) 槽遷移計劃
槽是Redis集群管理數(shù)據(jù)的基本單位,首先需要為新節(jié)點制定槽的遷移計劃,確定原有節(jié)點的哪些槽需要遷移新節(jié)點。
槽遷移計劃確定后開始逐個把槽內的數(shù)據(jù)從源節(jié)點遷移到目標節(jié)點。
具體流程如圖所示
2 遷移數(shù)據(jù)
數(shù)據(jù)遷移過程是逐個槽進行的:
流程說明:
1) 對目標節(jié)點發(fā)送cluster setslot {slot} importing {sourceNodeId}命令,讓目標節(jié)點準備導入槽的數(shù)據(jù)
2) 對源節(jié)點發(fā)送cluster setslot {slot} migrating {targetNodeId},讓源節(jié)點準備遷出槽的數(shù)據(jù)
3) 源節(jié)點循環(huán)執(zhí)行cluster getkeysinslot {slot} {count}命令。獲取count個屬于{slot}的槽
4)在源節(jié)點上執(zhí)行migrate {targetIp} {targetPort} "" 0 {timeout} keys {keys...}命令,把獲取的鍵通過流水線機制(pipeline)批量遷移到目標節(jié)點。
5)重復執(zhí)行步驟3 4 直到槽下所有的鍵值數(shù)據(jù)遷移到目標節(jié)點
6) 向集群內所有主節(jié)點發(fā)送cluster setslot {slot} node {targetNodeId}命令通知槽分配給目標節(jié)點。
但是一般我們不手動去遷移數(shù)據(jù),而是用redis-trib提供的槽重分片功能實現(xiàn)數(shù)據(jù)的遷移i。
具體操作是如何實現(xiàn)的可以查看課本。
10.4.3 收縮集群
收縮集群意味著縮減規(guī)模,需要從現(xiàn)有集群中安全下線部分節(jié)點。安全下線節(jié)點流程如下圖所示:
1 下線遷移槽
下線節(jié)點需要自己把自己負責的槽遷移到其他節(jié)點,原理與之前節(jié)點擴容的遷移槽過程一致。
2 忘記節(jié)點
由于集群內的節(jié)點不斷的通過Gossip消息彼此交換節(jié)點狀態(tài),因此需要通過一種健壯的機制讓集群內所有的節(jié)點忘記下線的節(jié)點。為此,Redis提供了cluster forget {downNodeId}實現(xiàn)該功能
線上操作不建議直接使用cluster forget執(zhí)行節(jié)點下線,建議使用redis-trib.rb del-node {host:port} {downNodeId}來執(zhí)行該操作。
10.5 請求路由
10.5.1 請求重定向
集群模式下,Redis接收任何鍵相關命令時首先計算鍵對應的槽,再根據(jù)槽找出所對應的節(jié)點,如果節(jié)點是自身,則處理鍵命令;否則回復MOVED重定向錯誤,通知客戶端請求正確的節(jié)點,這個過程稱為MOVED重定向;
如圖所示:
鍵命令執(zhí)行步驟主要分為兩步:計算槽,查找槽所對應的節(jié)點。
1 計算槽
Redis首先需要計算鍵所對應的槽。根據(jù)鍵的有效部分使用CRC16函數(shù)計算出散列值,再取對16383的余數(shù),使每個鍵都可以映射到0-16383槽范圍內
2 槽節(jié)點查詢
Redis計算得到鍵對應的槽后,需要查找槽所對應的節(jié)點。
根據(jù)MOVED重定向機制,客戶端可以隨機連接集群內任一Redis獲取鍵所在的節(jié)點,這種客戶端叫Dummy客戶端,它的代碼簡單,但是也存在一些問題。因此通常情況下,集群客戶端都采用另一種實現(xiàn):Smart客戶端。
10.5.2 Smart客戶端
實現(xiàn)原理:
Smart客戶端通過在內部維護slot->node的映射關系,本地就可以實現(xiàn)鍵到節(jié)點的查找,從而保證IO效率的最大化,而MOVED重定向機制負責協(xié)助Smart客戶端更新slot->node映射。
10.5.3 ASK重定向
10.6 故障轉移
Redis集群內節(jié)點通過ping/pong 命令實現(xiàn)節(jié)點通信,消息不但可以傳播節(jié)點槽信息,還可以傳播其他狀態(tài)如:主從狀態(tài)、節(jié)點故障等。因此故障發(fā)現(xiàn)也是通過消息傳播機制實現(xiàn)的,主要環(huán)節(jié)如下:主觀下線pfail和客觀下線fail
再次理解主管下線和客觀下線:
主觀下線:指某個節(jié)點認為另一個節(jié)點不可用,即下線狀態(tài),這個狀態(tài)并不是最終的故障判定,只能代表一個節(jié)點 的意見,可能存在誤判的情況。
客觀下線:標記一個節(jié)點真正的下線,集群內多個節(jié)點都認為i該節(jié)點不可用,從而達成共識的結果。如果是持有槽的主節(jié)點故障,需要為該節(jié)點執(zhí)行故障轉移。
Redis集群是比較健壯的故障發(fā)現(xiàn)機制,因此只有當認為客觀下線后,才認為該節(jié)點是真正的下線。
10.6.2 故障恢復
故障節(jié)點變?yōu)榭陀^下線后,如果該節(jié)點是持有槽的主節(jié)點,則需要在它的從節(jié)點中選出一個替換它,從而保證的集群的高可用。下線主節(jié)點的所有從節(jié)點承擔故障恢復的義務,當從節(jié)點通過內部定時任務發(fā)現(xiàn)自身復制的主節(jié)點進入客觀下線后時,將觸發(fā)故障恢復流程。
故障恢復流程如下:
10.7 集群運維
10.7.1 集群完整性
為了保證集群的完整性,默認情況下當前集群16384個槽任何一個沒有指派到節(jié)點時整個集群不可用。
10.7.2 帶寬消耗
集群內Gossip消息通信本身會消耗帶寬,官方建議集群內最大規(guī)模在1000以內,也是出于對消息通信成本的考慮,因此單集群不適合部署超大規(guī)模的節(jié)點。
10.7.3 Pub/Sub 廣播問題
Redis 提供訂閱發(fā)布功能,用于針對頻道實現(xiàn)消息的發(fā)布和訂閱,但是在集群模式下內部實現(xiàn)對所有的publish命令都會向所有的節(jié)點進行廣播,造成每條publish數(shù)據(jù)都會在集群內所有節(jié)點傳播一次,加重帶寬負擔。
10.7.4 集群傾斜
集群傾斜指不同節(jié)點之間的數(shù)據(jù)量和請求量出現(xiàn)明顯差異,這種情況將加大負載均衡和開發(fā)運維的難度。
分為數(shù)據(jù)傾斜和請求傾斜
10.7.5 集群讀寫分離
10.7.6 手動故障轉移
10.7.7 數(shù)據(jù)遷移
總結
以上是生活随笔為你收集整理的《Redis开发与运维》学习第十章的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【微信小程序制作模板套用】小程序模板如何
- 下一篇: 十二:内存简单介绍和OC的内存管理