新聞中心
1 集群的意義
從單機的一主多從復(fù)制架構(gòu)到現(xiàn)在的分布式架構(gòu)

成都做網(wǎng)站、網(wǎng)站制作、成都外貿(mào)網(wǎng)站建設(shè)服務(wù)團隊是一支充滿著熱情的團隊,執(zhí)著、敏銳、追求更好,是創(chuàng)新互聯(lián)的標(biāo)準(zhǔn)與要求,同時竭誠為客戶提供服務(wù)是我們的理念。創(chuàng)新互聯(lián)建站把每個網(wǎng)站當(dāng)做一個產(chǎn)品來開發(fā),精雕細(xì)琢,追求一名工匠心中的細(xì)致,我們更用心!
主要有如下維度:
- 業(yè)務(wù)
- 追求更高QPS
- 數(shù)據(jù)量
- Scale Up已經(jīng)無法滿足,超過了單機極限,考慮Scale Out分布式
- 網(wǎng)絡(luò)流量
- 業(yè)務(wù)流量超過服務(wù)器網(wǎng)卡上限,考慮分布式分流
- 離線計算
- 需要中間環(huán)節(jié)緩沖等需求
2 meet
節(jié)點之間完成相互通信的基礎(chǔ),有一定的頻率和規(guī)則。
CLUSTER MEET命令被用來連接不同的開啟集群支持的 Redis 節(jié)點,以進入工作集群。
2.1 基本思想
每個節(jié)點默認(rèn)都是相互不信任的,并且被認(rèn)為是未知的節(jié)點,以便萬一因為系統(tǒng)管理錯誤或地址被修改,而不太可能將多個不同的集群節(jié)點混成一個集群。
因此,為了使給定節(jié)點能將另一個節(jié)點接收到組成 Redis Cluster 的節(jié)點列表中,這里只有兩種方法:
- 系統(tǒng)管理員發(fā)送一個CLUSTER MEET命令強制一個節(jié)點會見另一個節(jié)點
- 一個已知節(jié)點發(fā)送一個保存在 gossip 部分的節(jié)點列表,包含著未知節(jié)點。如果接收的節(jié)點已經(jīng)將發(fā)送節(jié)點信任為已知節(jié)點,它會處理 gossip 部分并且發(fā)送一個握手消息給未知的節(jié)點。
Redis Cluster 需要形成一個完整的網(wǎng)絡(luò)(每個節(jié)點都連接著其他每個節(jié)點),但為創(chuàng)建一個集群,不需要發(fā)送形成網(wǎng)絡(luò)所需的所有CLUSTER MEET命令。發(fā)送CLUSTER MEET消息以便每個節(jié)點能夠到達(dá)其他每個節(jié)點只需通過一條已知的節(jié)點鏈就夠了。由于在心跳包中會交換 gossip 信息,將會創(chuàng)建節(jié)點間缺失的鏈接。
所以,如果我們通過CLUSTER MEET鏈接節(jié)點 A 和 B ,并且 B 和 C 有鏈接,那么節(jié)點 A 和 C 會發(fā)現(xiàn)他們握手和創(chuàng)建鏈接的方法。
2.2 案例
假設(shè)某一集群有A、B、C、D四個節(jié)點,可以只發(fā)送以下一組命令給 A :
- CLUSTER MEET B-ip B-port
- CLUSTER MEET C-ip C-port
- CLUSTER MEET D-ip D-port
由于 A 知道及被其他所有節(jié)點知道,它將會在發(fā)送的心跳包中包含gossip部分,這將允許其他每個節(jié)點彼此都創(chuàng)建一個鏈接,即使集群很大,也能在數(shù)秒內(nèi)形成一個完整網(wǎng)絡(luò)。
CLUSTER MEET無需相互執(zhí)行,即若發(fā)送命令給 A 以加入B ,那就不必也發(fā)送給 B 以加入 A。
2.3 實現(xiàn)細(xì)節(jié):MEET 和 PING 包
當(dāng)某一給定節(jié)點接收到一個MEET消息時,命令中指定的節(jié)點仍不知發(fā)送了該命令,所以為使節(jié)點強制將接收命令的節(jié)點將它作為信任的節(jié)點接受它,它會發(fā)送MEET包而非PING包。兩個消息包有相同的格式,但是MEET強制使接收消息包的節(jié)點確認(rèn)發(fā)送消息包的節(jié)點為可信任的。
3 指派槽
- 把16384個槽平分給節(jié)點管理,每個節(jié)點只對自己負(fù)責(zé)的槽進行讀寫
- 每個節(jié)點間都相互通信,所以每個節(jié)點都知道其它節(jié)點所管理槽的范圍
客戶端與指派槽
4 集群伸縮
集群的伸縮包括新節(jié)點的加入和舊節(jié)點退出。
4.1 加入新節(jié)點
Redis集群加入新節(jié)點主要如下幾步:
- 準(zhǔn)備新節(jié)點
啟動一個集群模式下的Redis節(jié)點
- 加入集群
通過與任意一集群中的節(jié)點握手加入新節(jié)點
- 遷移slot到新節(jié)點
再向新節(jié)點分配它負(fù)責(zé)的slot并向其遷移slot對應(yīng)數(shù)據(jù)。
由于Redis采用Gossip協(xié)議,所以可讓新節(jié)點與任一現(xiàn)有集群節(jié)點握手,一段時間后整個集群都會知道加入了新節(jié)點。
4.1.1 案例
向如下集群中新加入一個節(jié)點6385。由于負(fù)載均衡的要求,加入后四個節(jié)點每個節(jié)點負(fù)責(zé)4096個slots,但集群中原來的每個節(jié)點都負(fù)責(zé)5462個slots,所以6379、6380、6381節(jié)點都需要向新的節(jié)點6385遷移1366個slots。
Redis集群并沒有一個自動實現(xiàn)負(fù)載均衡的工具,把多少slots從哪個節(jié)點遷移到哪個節(jié)點完全是由用戶指定。
遷移數(shù)據(jù)的流程圖
遷移key可以用pipeline進行批量的遷移。
對于擴容,原理已經(jīng)很清晰了,至于具體操作,網(wǎng)上很多。
至于縮容,也是先手動完成數(shù)據(jù)遷移,再關(guān)閉redis。收縮時如果下線的節(jié)點有負(fù)責(zé)的槽需要遷移到其他節(jié)點,再通過cluster forget命令讓集群內(nèi)所有節(jié)點忘記被下線節(jié)點
5 客戶端路由
5.1 moved重定向
每個節(jié)點通信共享Redis Cluster中槽和集群中對應(yīng)節(jié)點的關(guān)系。
- 客戶端向Redis Cluster的任一節(jié)點發(fā)送命令
- 接收命令的節(jié)點再計算自己的槽和對應(yīng)節(jié)點
1.如果保存數(shù)據(jù)的槽被分配給當(dāng)前節(jié)點,則去槽中執(zhí)行命令,并把命令執(zhí)行結(jié)果返回給客戶端
2.如果保存數(shù)據(jù)的槽不在當(dāng)前節(jié)點的管理范圍內(nèi),則向客戶端返回moved重定向異常
3.客戶端接收到節(jié)點返回的結(jié)果,如果是moved異常,則從moved異常中獲取目標(biāo)節(jié)點的信息
4.客戶端向目標(biāo)節(jié)點發(fā)送命令,獲取命令執(zhí)行結(jié)果
客戶端不會自動找到目標(biāo)節(jié)點執(zhí)行命令,需要二次執(zhí)行
5.2 ask重定向
由于集群伸縮時,需要數(shù)據(jù)遷移。
當(dāng)客戶端訪問某key,節(jié)點告訴客戶端key在源節(jié)點,再去源節(jié)點訪問時,卻發(fā)現(xiàn)key已遷移到目標(biāo)節(jié)點,就會返回ask。
- 客戶端向目標(biāo)節(jié)點發(fā)送命令,目標(biāo)節(jié)點中的槽已經(jīng)遷移到其它節(jié)點
- 目標(biāo)節(jié)點會返回ask轉(zhuǎn)向給客戶端
- 客戶端向新節(jié)點發(fā)送Asking命令
- 再向新節(jié)點發(fā)送命令
- 新節(jié)點執(zhí)行命令,把命令執(zhí)行結(jié)果返回給客戶端
為什么不能簡單使用MOVED重定向?
因為雖然MOVED意味著我們認(rèn)為哈希槽由另一個節(jié)點永久提供,并且應(yīng)該對指定節(jié)點嘗試下一個查詢,所以ASK意味著僅將下一個查詢發(fā)送到指定節(jié)點。
之所以需要這樣做,是因為下一個關(guān)于哈希槽的查詢可能是關(guān)于仍在A中的鍵的,因此我們始終希望客戶端嘗試A,然后在需要時嘗試B。由于只有16384個可用的哈希槽中有一個發(fā)生,因此群集上的性能下降是可以接受的。
5.3 moved V.S ask
都是客戶端重定向
- moved:槽已經(jīng)確定轉(zhuǎn)移
- ask:槽還在遷移中
5.4 smart智能客戶端
目標(biāo)
追求性能
設(shè)計思路
- 從集群中選一個可運行節(jié)點,使用Cluster slots初始化槽和節(jié)點映射
- 將Cluster slots的結(jié)果映射在本地,為每個節(jié)點創(chuàng)建JedisPool,然后就可以進行數(shù)據(jù)讀寫操作
注意事項
- 每個JedisPool中緩存了slot和節(jié)點node的關(guān)系
- key和slot的關(guān)系:對key進行CRC16規(guī)則進行hash后與16383取余得到的結(jié)果就是槽
- JedisCluster啟動時,已經(jīng)知道key,slot和node之間的關(guān)系,可以找到目標(biāo)節(jié)點
- JedisCluster對目標(biāo)節(jié)點發(fā)送命令,目標(biāo)節(jié)點直接響應(yīng)給JedisCluster
- 如果JedisCluster與目標(biāo)節(jié)點連接出錯,則JedisCluster會知道連接的節(jié)點是一個錯誤的節(jié)點
- 此時JedisCluster會隨機節(jié)點發(fā)送命令,隨機節(jié)點返回moved異常給JedisCluster
- JedisCluster會重新初始化slot與node節(jié)點的緩存關(guān)系,然后向新的目標(biāo)節(jié)點發(fā)送命令,目標(biāo)命令執(zhí)行命令并向JedisCluster響應(yīng)
- 如果命令發(fā)送次數(shù)超過5次,則拋出異常"Too many cluster redirection!"
- 基本圖示
全面圖示
6 批量操作
mget、mset須在同一槽。
Redis Cluster不同于Redis 單節(jié)點,甚至和一個 Sentinel 監(jiān)控的主從模式也不一樣。主要是因為集群自動分片,將一個key 映射到16384槽之一,這些槽分布在多節(jié)。因此操作多 key 的命令必須保證所有的key都映射同一槽,避免跨槽執(zhí)行錯誤。
一個單獨的集群節(jié)點,只服務(wù)一組專用的keys,請求一個命令到一個Server,只能得到該Server上擁有keys的對應(yīng)結(jié)果。
一個非常簡單的例子是執(zhí)行KEYS命令,當(dāng)發(fā)布該命令到集群中某節(jié)點時,只能得到該節(jié)點上擁有key,并非集群中所有key。要得到集群中所有key,必須從集群的所有主節(jié)點上獲取所有key。
對于分散在redis集群中不同節(jié)點的數(shù)據(jù),我們?nèi)绾伪容^高效地批量獲取數(shù)據(jù)呢?
6.1 串行mget
定義for循環(huán),遍歷所有key,分別去所有的Redis節(jié)點中獲取值并進行匯總,簡單,但效率不高,需n次網(wǎng)絡(luò)時間。
6.2 串行I/O
優(yōu)化串行的mget,在客戶端本地做內(nèi)聚,對每個key hash,然后取余,知道key對應(yīng)槽
本地已緩存了槽與節(jié)點的對應(yīng)關(guān)系,然后對key按節(jié)點進行分組,成立子集,然后使用pipeline把命令發(fā)送到對應(yīng)的node,需要nodes次網(wǎng)絡(luò)時間,大大減少了網(wǎng)絡(luò)時間開銷。
6.3 并行I/O
優(yōu)化串行IO,分組key后,根據(jù)節(jié)點數(shù)量啟動對應(yīng)的線程數(shù),根據(jù)多線程模式并行向node節(jié)點請求數(shù)據(jù),只需1次網(wǎng)絡(luò)時間
6.4 hash_tag
不做任何改變,hash后就比較均勻地散在每個節(jié)點上
是否能像單機,一次IO將所有key取出呢?hash-tag提供了這樣功能:若將上述key改為如下,即大括號括起來相同的內(nèi)容,保證所有的key只向一個node請求數(shù)據(jù),這樣執(zhí)行類似mget命令只需要去一個節(jié)點獲取數(shù)據(jù)即可,效率更高。
6.5 選型對比
第一種方式,使用多線程解決批量問題,減少帶寬時延,提高效率,這種做法就如上面所說簡單便捷(我們目前批量操作類型比較多),有效。但問題比較明顯。批量操作數(shù)量不大即可滿足。搜狐的cachecloud采用第二點,先將key獲取槽點,然后分node pipeline操作。這種做法相對比第一種做法較優(yōu)。
7 故障轉(zhuǎn)移
7.1 故障發(fā)現(xiàn)
Redis Cluster通過ping/pong消息實現(xiàn)故障發(fā)現(xiàn):不需要sentinel。ping/pong不僅能傳遞節(jié)點與槽的對應(yīng)消息,也能傳遞其他狀態(tài),比如:節(jié)點主從狀態(tài),節(jié)點故障等
故障發(fā)現(xiàn)就是通過這種模式來實現(xiàn),分為主觀下線和客觀下線:
7.1.1 主觀下線
定義
某節(jié)點認(rèn)為另一節(jié)點不可用,這僅代表一個節(jié)點對另一節(jié)點的判斷,不代表所有節(jié)點的認(rèn)知。
流程
- 節(jié)點-1定時發(fā)ping消息給節(jié)點-2
- 若發(fā)送成功,代表節(jié)點-2正常運行,節(jié)點-2會響應(yīng)PONG消息給節(jié)點1,節(jié)點1更新與2的最后通信時間
- 若發(fā)送失敗,則節(jié)點-1與節(jié)點-2間通信異常判斷連接,在下一定時任務(wù)周期時,仍然會與節(jié)點2發(fā)送ping消息
- 若節(jié)點-1發(fā)現(xiàn)與節(jié)點-2最后通信時間超過node-timeout,則把節(jié)點2標(biāo)識為pfail狀態(tài)
7.1.2 客觀下線
定義
當(dāng)半數(shù)以上持有槽的主節(jié)點都標(biāo)記某節(jié)點主觀下線??梢员WC判斷的公平性。
集群模式下,只有主節(jié)點(master)才有讀寫權(quán)限和集群槽的維護權(quán)限,從節(jié)點(slave)只有復(fù)制的權(quán)限。
流程
1.某個節(jié)點接收到其他節(jié)點發(fā)送的ping消息,如果接收到的ping消息中包含了其他pfail節(jié)點,這個節(jié)點會將主觀下線的消息內(nèi)容添加到自身的故障列表中,故障列表中包含了當(dāng)前節(jié)點接收到的每一個節(jié)點對其他節(jié)點的狀態(tài)信息
2.當(dāng)前節(jié)點把主觀下線的消息內(nèi)容添加到自身的故障列表之后,會嘗試對故障節(jié)點進行客觀下線操作
7.2 故障恢復(fù)
從節(jié)點接收到它的主節(jié)點客觀下線的通知,則進行故障恢復(fù)。
資格檢查
- 對從節(jié)點的資格進行檢查,只有難過檢查的從節(jié)點才可以開始進行故障恢復(fù)
- 每個從節(jié)點檢查與故障主節(jié)點的斷線時間
- 超過cluster-node-timeout * cluster-slave-validity-factor數(shù)字,則取消資格
- cluster-node-timeout默認(rèn)為15秒,cluster-slave-validity-factor默認(rèn)值為10
- 如果這兩個參數(shù)都使用默認(rèn)值,則每個節(jié)點都檢查與故障主節(jié)點的斷線時間,如果超過150秒,則這個節(jié)點就沒有成為替換主節(jié)點的可能性
準(zhǔn)備選舉時間
使偏移量最大的從節(jié)點具備優(yōu)先級成為主節(jié)點的條件。
選舉投票
對選舉出來的多個從節(jié)點進行投票,選出新的主節(jié)點。
替換主節(jié)點
- 當(dāng)前從節(jié)點取消復(fù)制變成離節(jié)點。(slaveof no one)
- 執(zhí)行cluster del slot撤銷故障主節(jié)點負(fù)責(zé)的槽,并執(zhí)行cluster add slot把這些槽分配給自己
- 向集群廣播自己的pong消息,表明已經(jīng)替換了故障從節(jié)點
8 開發(fā)運維常見問題
8.1 集群完整性
- cluster-require-full-coverage默認(rèn)為yes,即集群中所有節(jié)點都在服務(wù)且16384個槽都可用,集群才會提供服務(wù),以保證集群完整性。
- 當(dāng)某節(jié)點故障或者正在故障轉(zhuǎn)移時獲取數(shù)據(jù)會提示:(error)CLUSTERDOWN The cluster is down
但是大多數(shù)業(yè)務(wù)都無法容忍,建議把cluster-require-full-coverage設(shè)為no
8.2 帶寬消耗
- Redis Cluster節(jié)點之間會定期交換Gossip消息,以及做一些心跳檢測
- 官方建議Redis Cluster節(jié)點數(shù)量不要超過1000個,當(dāng)集群中節(jié)點數(shù)量過多時,會產(chǎn)生不容忽視的帶寬消耗
- 消息發(fā)送頻率:節(jié)點發(fā)現(xiàn)與其他節(jié)點最后通信時間超過cluster-node-timeout /2時,會直接發(fā)送PING消息
- 消息數(shù)據(jù)量:slots槽數(shù)組(2kb空間)和整個集群1/10的狀態(tài)數(shù)據(jù)(10個節(jié)點狀態(tài)數(shù)據(jù)約為1kb)
- 節(jié)點部署的機器規(guī)模:集群分布的機器越多且每臺機器劃分的節(jié)點數(shù)越均勻,則集群內(nèi)整體的可用帶寬越高
帶寬優(yōu)化
- 避免使用’大’集群:避免多業(yè)務(wù)使用一個集群,大業(yè)務(wù)可以多集群
- cluster-node-timeout:帶寬和故障轉(zhuǎn)移速度的均衡
- 盡量均勻分配到多機器上:保證高可用和帶寬
8.3 Pub/Sub廣播
- 在任意一個cluster節(jié)點執(zhí)行publish,則發(fā)布的消息會在集群中傳播,集群中的其他節(jié)點都會訂閱到消息,這樣節(jié)點的帶寬的開銷會很大
- publish在集群每個節(jié)點廣播,加重帶寬
解決方案
單獨“走”一套redis sentinel。就是針對目標(biāo)的幾個節(jié)點構(gòu)建redis sentinel,在這個里面實現(xiàn)廣播。
8.4 集群傾斜
分布式數(shù)據(jù)庫存在傾斜問題是比較常見的。集群傾斜也就是各個節(jié)點使用的內(nèi)存不一致
數(shù)據(jù)傾斜原因
1.節(jié)點和槽分配不均,如果使用redis-trib.rb工具構(gòu)建集群,則出現(xiàn)這種情況的機會不多
- redis-trib.rb info ip:port查看節(jié)點,槽,鍵值分布
- redis-trib.rb rebalance ip:port進行均衡(謹(jǐn)慎使用)
2.不同槽對應(yīng)鍵值數(shù)量差異比較大
- CRC16算法正常情況下比較均勻
- 可能存在hash_tag
- cluster countkeysinslot {slot}獲取槽對應(yīng)鍵值個數(shù)
3.包含bigkey:例如大字符串,幾百萬的元素的hash,set等
- 在從節(jié)點:redis-cli --bigkeys
- 優(yōu)化:優(yōu)化數(shù)據(jù)結(jié)構(gòu)
4.內(nèi)存相關(guān)配置不一致
- hash-max-ziplist-value:滿足一定條件情況下,hash可以使用ziplist
- set-max-intset-entries:滿足一定條件情況下,set可以使用intset
- 在一個集群內(nèi)有若干個節(jié)點,當(dāng)其中一些節(jié)點配置上面兩項優(yōu)化,另外一部分節(jié)點沒有配置上面兩項優(yōu)化
- 當(dāng)集群中保存hash或者set時,就會造成節(jié)點數(shù)據(jù)不均勻
- 優(yōu)化:定期檢查配置一致性
5.請求傾斜:熱點key
重要的key或者bigkey
Redis Cluster某個節(jié)點有一個非常重要的key,就會存在熱點問題
優(yōu)化
- 避免bigkey
- 熱鍵不要用hash_tag
- 當(dāng)一致性不高時,可以用本地緩存+ MQ(消息隊列)
9 讀寫分離
- 只讀連接
- 集群模式下,從節(jié)點不接受任何讀寫請求。
- 當(dāng)向從節(jié)點執(zhí)行讀請求時,重定向到負(fù)責(zé)槽的主節(jié)點
- readonly命令可以讀:連接級別命令,當(dāng)連接斷開之后,需要再次執(zhí)行
- readonlyredis cluster 默認(rèn)slave 也是不能讀的,如果要讀取,需要執(zhí)行 readonly,就可以了。
讀寫分離:更加復(fù)雜(成本很高,盡量不要使用)
- 同樣的問題:復(fù)制延遲,讀取過期數(shù)據(jù),從節(jié)點故障
- 修改客戶端:cluster slaves {nodeId}
10 集群優(yōu)劣
集群的限制
- key批量操作支持有限:例如mget,mset必須在一個slot
- key事務(wù)和Lua支持有限:操作的key必須在一個節(jié)點
- key是數(shù)據(jù)分區(qū)的最小粒度:不支持bigkey分區(qū)
- 不支持多個數(shù)據(jù)庫:集群模式下只有一個db0
- 復(fù)制只支持一層:不支持樹形復(fù)制結(jié)構(gòu)
集群不一定好
- Redis Cluster滿足容量和性能的擴展性,很多業(yè)務(wù)’不需要’
- 大多數(shù)時客戶端性能會’降低’
- 命令無法跨節(jié)點使用:mget,keys,scan,flush,sinter等
- Lua和事務(wù)無法跨節(jié)點使用
- 客戶端維護更復(fù)雜:SDK和應(yīng)用本身消耗(例如更多的連接池)
- 很多場景Redis Sentinel已經(jīng)夠用了
參考
https://www.slideshare.net/iammutex/redis-cluster
https://zhuanlan.zhihu.com/p/105569485
https://sunweiguo.github.io/2019/02/01/redis/Redis-Cluster%E7%90%86%E8%AE%BA%E8%AF%A6%E8%A7%A3/
https://redis.io/topics/cluster-spec
http://trumandu.github.io/2016/05/09/RedisCluster%E6%9E%84%E5%BB%BA%E6%89%B9%E9%87%8F%E6%93%8D%E4%BD%9C%E6%8E%A2%E8%AE%A8/
新聞標(biāo)題:一文講清RedisCluster
網(wǎng)站URL:http://www.5511xx.com/article/dpocgeo.html


咨詢
建站咨詢
