Redis Cluster是Redis官方提供的Redis集羣功能php
1.爲何要實現Redis Cluster
1.主從複製不能實現高可用 2.隨着公司發展,用戶數量增多,併發愈來愈多,業務須要更高的QPS,而主從複製中單機的QPS可能沒法知足業務需求 3.數據量的考慮,現有服務器內存不能知足業務數據的須要時,單純向服務器添加內存不能達到要求,此時須要考慮分佈式需求,把數據分佈到不一樣服務器上 4.網絡流量需求:業務的流量已經超過服務器的網卡的上限值,能夠考慮使用分佈式來進行分流 5.離線計算,須要中間環節緩衝等別的需求
2.數據分佈
2.1 爲何要作數據分佈
全量數據,單機Redis節點沒法知足要求,按照分區規則把數據分到若干個子集當中css
2.2 經常使用數據分佈方式之順序分佈
好比:1到100個數字,要保存在3個節點上,按照順序分區,把數據平均分配三個節點上 1號到33號數據保存到節點1上,34號到66號數據保存到節點2上,67號到100號數據保存到節點3上
順序分區經常使用在關係型數據庫的設計html
2.3 經常使用數據分佈方式之哈希分佈
例如1到100個數字,對每一個數字進行哈希運算,而後對每一個數的哈希結果除以節點數進行取餘,餘數爲1則保存在第1個節點上,餘數爲2則保存在第2個節點上,餘數爲0則保存在第3個節點,這樣能夠保證數據被打散,同時保證數據分佈的比較均勻
哈希分佈方式分爲三個分區方式:node
2.3.1 節點取餘分區
好比有100個數據,對每一個數據進行hash運算以後,與節點數進行取餘運算,根據餘數不一樣保存在不一樣的節點上python
節點取餘方式是很是簡單的一種分區方式mysql
節點取餘分區方式有一個問題:即當增長或減小節點時,原來節點中的80%的數據會進行遷移操做,對全部數據從新進行分佈linux
節點取餘分區方式建議使用多倍擴容的方式,例如之前用3個節點保存數據,擴容爲比之前多一倍的節點即6個節點來保存數據,這樣只須要適移50%的數據。數據遷移以後,第一次沒法從緩存中讀取數據,必須先從數據庫中讀取數據,而後回寫到緩存中,而後才能從緩存中讀取遷移以後的數據redis
節點取餘方式優勢:算法
客戶端分片 配置簡單:對數據進行哈希,而後取餘
節點取餘方式缺點:sql
數據節點伸縮時,致使數據遷移 遷移數量和添加節點數據有關,建議翻倍擴容
2.3.2 一致性哈希分區
一致性哈希原理:
將全部的數據當作一個token環,token環中的數據範圍是0到2的32次方。而後爲每個數據節點分配一個token範圍值,這個節點就負責保存這個範圍內的數據。
對每個key進行hash運算,被哈希後的結果在哪一個token的範圍內,則按順時針去找最近的節點,這個key將會被保存在這個節點上。
在上面的圖中,有4個key被hash以後的值在在n1節點和n2節點之間,按照順時針規則,這4個key都會被保存在n2節點上, 若是在n1節點和n2節點之間添加n5節點,當下次有key被hash以後的值在n1節點和n5節點之間,這些key就會被保存在n5節點上面了 在上面的例子裏,添加n5節點以後,數據遷移會在n1節點和n2節點之間進行,n3節點和n4節點不受影響,數據遷移範圍被縮小不少 同理,若是有1000個節點,此時添加一個節點,受影響的節點範圍最多隻有千分之2 一致性哈希通常用在節點比較多的時候
一致性哈希分區優勢:
採用客戶端分片方式:哈希 + 順時針(優化取餘) 節點伸縮時,隻影響鄰近節點,可是仍是有數據遷移
一致性哈希分區缺點:
翻倍伸縮,保證最小遷移數據和負載均衡
2.3.3 虛擬槽分區
虛擬槽分區是Redis Cluster採用的分區方式
預設虛擬槽,每一個槽就至關於一個數字,有必定範圍。每一個槽映射一個數據子集,通常比節點數大
Redis Cluster中預設虛擬槽的範圍爲0到16383
步驟:
1.把16384槽按照節點數量進行平均分配,由節點進行管理
2.對每一個key按照CRC16規則進行hash運算 3.把hash結果對16383進行取餘 4.把餘數發送給Redis節點 5.節點接收到數據,驗證是否在本身管理的槽編號的範圍 若是在本身管理的槽編號範圍內,則把數據保存到數據槽中,而後返回執行結果 若是在本身管理的槽編號範圍外,則會把數據發送給正確的節點,由正確的節點來把數據保存在對應的槽中
須要注意的是:Redis Cluster的節點之間會共享消息,每一個節點都會知道是哪一個節點負責哪一個範圍內的數據槽
虛擬槽分佈方式中,因爲每一個節點管理一部分數據槽,數據保存到數據槽中。當節點擴容或者縮容時,對數據槽進行從新分配遷移便可,數據不會丟失。
虛擬槽分區特色:
使用服務端管理節點,槽,數據:例如Redis Cluster 能夠對數據打散,又能夠保證數據分佈均勻
2.3 順序分佈與哈希分佈的對比
3.Redis Cluster基本架構
3.1 節點
Redis Cluster是分佈式架構:即Redis Cluster中有多個節點,每一個節點都負責進行數據讀寫操做
每一個節點之間會進行通訊。
3.2 meet操做
節點之間會相互通訊
meet操做是節點之間完成相互通訊的基礎,meet操做有必定的頻率和規則
3.3 分配槽
把16384個槽平均分配給節點進行管理,每一個節點只能對本身負責的槽進行讀寫操做
因爲每一個節點之間都彼此通訊,每一個節點都知道另外節點負責管理的槽範圍
客戶端訪問任意節點時,對數據key按照CRC16規則進行hash運算,而後對運算結果對16383進行取做,若是餘數在當前訪問的節點管理的槽範圍內,則直接返回對應的數據
若是不在當前節點負責管理的槽範圍內,則會告訴客戶端去哪一個節點獲取數據,由客戶端去正確的節點獲取數據
3.4 複製
保證高可用,每一個主節點都有一個從節點,當主節點故障,Cluster會按照規則實現主備的高可用性
對於節點來講,有一個配置項:cluster-enabled,便是否以集羣模式啓動
3.5 客戶端路由
3.5.1 moved重定向
1.每一個節點經過通訊都會共享Redis Cluster中槽和集羣中對應節點的關係
2.客戶端向Redis Cluster的任意節點發送命令,接收命令的節點會根據CRC16規則進行hash運算與16383取餘,計算本身的槽和對應節點 3.若是保存數據的槽被分配給當前節點,則去槽中執行命令,並把命令執行結果返回給客戶端 4.若是保存數據的槽不在當前節點的管理範圍內,則向客戶端返回moved重定向異常 5.客戶端接收到節點返回的結果,若是是moved異常,則從moved異常中獲取目標節點的信息 6.客戶端向目標節點發送命令,獲取命令執行結果
須要注意的是:客戶端不會自動找到目標節點執行命令
槽命中:直接返回
[root@mysql ~]# redis-cli -p 9002 cluster keyslot hello (integer) 866
槽不命中:moved異常
[root@mysql ~]# redis-cli -p 9002 cluster keyslot php (integer) 9244
[root@mysql ~]# redis-cli -c -p 9002 127.0.0.1:9002> cluster keyslot hello (integer) 866 127.0.0.1:9002> set hello world -> Redirected to slot [866] located at 192.168.81.100:9003 OK 192.168.81.100:9003> cluster keyslot python (integer) 7252 192.168.81.100:9003> set python best -> Redirected to slot [7252] located at 192.168.81.101:9002 OK 192.168.81.101:9002> get python "best" 192.168.81.101:9002> get hello -> Redirected to slot [866] located at 192.168.81.100:9003 "world" 192.168.81.100:9003> exit [root@mysql ~]# redis-cli -p 9002 127.0.0.1:9002> cluster keyslot python (integer) 7252 127.0.0.1:9002> set python best OK 127.0.0.1:9002> set hello world (error) MOVED 866 192.168.81.100:9003 127.0.0.1:9002> exit [root@mysql ~]#
3.5.2 ask重定向
在對集羣進行擴容和縮容時,須要對槽及槽中數據進行遷移
當客戶端向某個節點發送命令,節點向客戶端返回moved異常,告訴客戶端數據對應的槽的節點信息
若是此時正在進行集羣擴展或者縮空操做,當客戶端向正確的節點發送命令時,槽及槽中數據已經被遷移到別的節點了,就會返回ask,這就是ask重定向機制
步驟:
1.客戶端向目標節點發送命令,目標節點中的槽已經遷移支別的節點上了,此時目標節點會返回ask轉向給客戶端 2.客戶端向新的節點發送Asking命令給新的節點,而後再次向新節點發送命令 3.新節點執行命令,把命令執行結果返回給客戶端
moved異常與ask異常的相同點和不一樣點
二者都是客戶端重定向 moved異常:槽已經肯定遷移,即槽已經不在當前節點 ask異常:槽還在遷移中
3.5.3 smart智能客戶端
使用智能客戶端的首要目標:追求性能
從集羣中選一個可運行節點,使用Cluster slots初始化槽和節點映射
將Cluster slots的結果映射在本地,爲每一個節點建立JedisPool,至關於爲每一個redis節點都設置一個JedisPool,而後就能夠進行數據讀寫操做
讀寫數據時的注意事項:
每一個JedisPool中緩存了slot和節點node的關係
key和slot的關係:對key進行CRC16規則進行hash後與16383取餘獲得的結果就是槽 JedisCluster啓動時,已經知道key,slot和node之間的關係,能夠找到目標節點 JedisCluster對目標節點發送命令,目標節點直接響應給JedisCluster 若是JedisCluster與目標節點鏈接出錯,則JedisCluster會知道鏈接的節點是一個錯誤的節點 此時JedisCluster會隨機節點發送命令,隨機節點返回moved異常給JedisCluster JedisCluster會從新初始化slot與node節點的緩存關係,而後向新的目標節點發送命令,目標命令執行命令並向JedisCluster響應 若是命令發送次數超過5次,則拋出異常"Too many cluster redirection!"
3.6 多節點命令實現
Redis Cluster不支持使用scan命令掃描全部節點
多節點命令就是在在全部節點上都執行一條命令
批量操做優化
3.6.1 串行mget
定義for循環,遍歷全部的key,分別去全部的Redis節點中獲取值並進行彙總,簡單,可是效率不高,須要n次網絡時間
3.6.2 串行IO
對串行mget進行優化,在客戶端本地作內聚,對每一個key進行CRC16hash,而後與16383取餘,就能夠知道哪一個key對應的是哪一個槽
本地已經緩存了槽與節點的對應關係,而後對key按節點進行分組,成立子集,而後使用pipeline把命令發送到對應的node,須要nodes次網絡時間,大大減小了網絡時間開銷
3.6.3 並行IO
並行IO是對串行IO的一個優化,把key分組以後,根據節點數量啓動對應的線程數,根據多線程模式並行向node節點請求數據,只須要1次網絡時間
3.6.4 hash_tag
將key進行hash_tag的包裝,而後把tag用大括號括起來,保證全部的key只向一個node請求數據,這樣執行相似mget命令只須要去一個節點獲取數據便可,效率更高
3.6.5 四種優化方案優缺點分析
3.7 故障發現
Redis Cluster經過ping/pong消息實現故障發現:不須要sentinel
ping/pong不只能傳遞節點與槽的對應消息,也能傳遞其餘狀態,好比:節點主從狀態,節點故障等
故障發現就是經過這種模式來實現,分爲主觀下線和客觀下線
3.7.1 主觀下線
某個節點認爲另外一個節點不可用,'偏見',只表明一個節點對另外一個節點的判斷,不表明全部節點的認知
主觀下線流程:
1.節點1按期發送ping消息給節點2 2.若是發送成功,表明節點2正常運行,節點2會響應PONG消息給節點1,節點1更新與節點2的最後通訊時間 3.若是發送失敗,則節點1與節點2之間的通訊異常判斷鏈接,在下一個定時任務週期時,仍然會與節點2發送ping消息 4.若是節點1發現與節點2最後通訊時間超過node-timeout,則把節點2標識爲pfail狀態
3.7.2 客觀下線
當半數以上持有槽的主節點都標記某節點主觀下線時,能夠保證判斷的公平性
集羣模式下,只有主節點(master)纔有讀寫權限和集羣槽的維護權限,從節點(slave)只有複製的權限
客觀下線流程:
1.某個節點接收到其餘節點發送的ping消息,若是接收到的ping消息中包含了其餘pfail節點,這個節點會將主觀下線的消息內容添加到自身的故障列表中,故障列表中包含了當前節點接收到的每個節點對其餘節點的狀態信息 2.當前節點把主觀下線的消息內容添加到自身的故障列表以後,會嘗試對故障節點進行客觀下線操做
故障列表的週期爲:集羣的node-timeout * 2,保證之前的故障消息不會對週期內的故障消息形成影響,保證客觀下線的公平性和有效性
3.8 故障恢復
3.8.1 資格檢查
對從節點的資格進行檢查,只有難過檢查的從節點才能夠開始進行故障恢復 每一個從節點檢查與故障主節點的斷線時間 超過cluster-node-timeout * cluster-slave-validity-factor數字,則取消資格 cluster-node-timeout默認爲15秒,cluster-slave-validity-factor默認值爲10 若是這兩個參數都使用默認值,則每一個節點都檢查與故障主節點的斷線時間,若是超過150秒,則這個節點就沒有成爲替換主節點的可能性
3.9.2 準備選舉時間
使偏移量最大的從節點具有優先級成爲主節點的條件
3.8.3 選舉投票
對選舉出來的多個從節點進行投票,選出新的主節點
3.8.4 替換主節點
當前從節點取消複製變成離節點(slaveof no one)
執行cluster del slot撤銷故障主節點負責的槽,並執行cluster add slot把這些槽分配給本身 向集羣廣播本身的pong消息,代表已經替換了故障從節點
3.8.5 故障轉移演練
對某一個主節點執行kill -9 {pid}來模擬宕機的狀況
3.9 Redis Cluster的缺點
當節點數量不少時,性能不會很高 解決方式:使用智能客戶端。智能客戶端知道由哪一個節點負責管理哪一個槽,並且當節點與槽的映射關係發生改變時,客戶端也會知道這個改變,這是一種很是高效的方式
4.搭建Redis Cluster
搭建Redis Cluster有兩種安裝方式
cluster-require-full-coverage默認爲yes,便是否集羣中的全部節點都是在線狀態且16384個槽都處於服務狀態時,集羣纔會提供服務
集羣中16384個槽所有處於服務狀態,保證集羣完整性
當某個節點故障或者正在故障轉移時獲取數據會提示:(error)CLUSTERDOWN The cluster is down
建議把cluster-require-full-coverage設置爲no
5.2 帶寬消耗
Redis Cluster節點之間會按期交換Gossip消息,以及作一些心跳檢測
官方建議Redis Cluster節點數量不要超過1000個,當集羣中節點數量過多時,會產生不容忽視的帶寬消耗
消息發送頻率:節點發現與其餘節點最後通訊時間超過cluster-node-timeout /2時,會直接發送PING消息
消息數據量:slots槽數組(2kb空間)和整個集羣1/10的狀態數據(10個節點狀態數據約爲1kb)
節點部署的機器規模:集羣分佈的機器越多且每臺機器劃分的節點數越均勻,則集羣內總體的可用帶寬越高
帶寬優化:
避免使用'大'集羣:避免多業務使用一個集羣,大業務能夠多集羣 cluster-node-timeout:帶寬和故障轉移速度的均衡 儘可能均勻分配到多機器上:保證高可用和帶寬
5.3 Pub/Sub廣播
在任意一個cluster節點執行publish,則發佈的消息會在集羣中傳播,集羣中的其餘節點都會訂閱到消息,這樣節點的帶寬的開銷會很大
publish在集羣每一個節點廣播,加劇帶寬
解決辦法:須要使用Pub/Sub時,爲了保證高可用,能夠單獨開啓一套Redis Sentinel
5.4 集羣傾斜
對於分佈式數據庫來講,存在傾斜問題是比較常見的
集羣傾斜也就是各個節點使用的內存不一致
5.4.1 數據傾斜緣由
1.節點和槽分配不均,若是使用redis-trib.rb工具構建集羣,則出現這種狀況的機會很少
redis-trib.rb info ip:port查看節點,槽,鍵值分佈 redis-trib.rb rebalance ip:port進行均衡(謹慎使用)
2.不一樣槽對應鍵值數量差別比較大
CRC16算法正常狀況下比較均勻 可能存在hash_tag cluster countkeysinslot {slot}獲取槽對應鍵值個數
3.包含bigkey:例如大字符串,幾百萬的元素的hash,set等
在從節點:redis-cli --bigkeys 優化:優化數據結構
4.內存相關配置不一致
hash-max-ziplist-value:知足必定條件狀況下,hash可使用ziplist set-max-intset-entries:知足必定條件狀況下,set可使用intset 在一個集羣內有若干個節點,當其中一些節點配置上面兩項優化,另一部分節點沒有配置上面兩項優化 當集羣中保存hash或者set時,就會形成節點數據不均勻 優化:按期檢查配置一致性
5.請求傾斜:熱點key
重要的key或者bigkey Redis Cluster某個節點有一個很是重要的key,就會存在熱點問題
5.4.2 集羣傾斜優化:
避免bigkey 熱鍵不要用hash_tag 當一致性不高時,能夠用本地緩存+ MQ(消息隊列)
5.5 集羣讀寫分離
只讀鏈接:集羣模式下,從節點不接受任何讀寫請求
當向從節點執行讀請求時,重定向到負責槽的主節點
readonly命令能夠讀:鏈接級別命令,當鏈接斷開以後,須要再次執行readonly命令
讀寫分離:
一樣的問題:複製延遲,讀取過時數據,從節點故障 修改客戶端:cluster slaves {nodeId}
5.6 數據遷移
官方遷移工具:redis-trib.rb和import
只能從單機遷移到集羣
不支持在線遷移:source須要停寫
不支持斷點續傳
單線程遷移:影響深度
在線遷移:
惟品會:redis-migrate-tool
豌豆莢:redis-port
5.7 集羣VS單機
集羣的限制:
key批量操做支持有限:例如mget,mset必須在一個slot key事務和Lua支持有限:操做的key必須在一個節點 key是數據分區的最小粒度:不支持bigkey分區 不支持多個數據庫:集羣模式下只有一個db0 複製只支持一層:不支持樹形複製結構 Redis Cluster知足容量和性能的擴展性,不少業務'不須要' 大多數時客戶端性能會'下降' 命令沒法跨節點使用:mget,keys,scan,flush,sinter等 Lua和事務沒法跨節點使用 客戶端維護更復雜:SDK和應用自己消耗(例如更多的鏈接池)
不少場景Redis Sentinel已經夠用了
6.Redis Cluster總結:
1.Redis Cluster數據分區規則採用虛擬槽方式(16384個槽),每一個節點負責一部分槽和相關數據,實現數據和請求的負載均衡 2.搭建Redis Cluster劃分四個步驟:準備節點,meet操做,分配槽,複製數據。 3.Redis官方推薦使用redis-trib.rb工具快速搭建Redis Cluster 4.集羣伸縮經過在節點之間移動槽和相關數據實現 擴容時根據槽遷移計劃把槽從源節點遷移到新節點 收縮時若是下線的節點有負責的槽須要遷移到其餘節點,再經過cluster forget命令讓集羣內全部節點忘記被下線節點 5.使用smart客戶端操做集羣過到通訊效率最大化,客戶端內部負責計算維護鍵,槽以及節點的映射,用於快速定位到目標節點 6.集羣自動故障轉移過程分爲故障發現和節點恢復。節點下線分爲主觀下線和客觀下線,當超過半數節點認爲故障節點爲主觀下線時,標記這個節點爲客觀下線狀態。從節點負責對客觀下線的主節點觸發故障恢復流程,保證集羣的可用性 7.開發運維常見問題包括:超大規模集羣帶席消耗,pub/sub廣播問題,集羣傾斜問題,單機和集羣對比等