【Redis】《Redis 开发与运维》笔记-Chapter10-集群
十、集群
1、概述
Redis Cluster是Redis 的分布式解決方案,在3.0版本正式推出,有效地解決了Redis分布式方面的需求。
Redis在3.0版本之前分布式方案一般有兩種:
- 客戶端分區(qū)方案,優(yōu)點是分區(qū)邏輯可控,缺點是需要自己處理數(shù)據(jù)路由、高可用、故障轉(zhuǎn)移等問題。
- 代理方案,優(yōu)點是簡化客戶端分布式邏輯和升級維護(hù)便利,缺點是加重架構(gòu)部署復(fù)雜度和性能損耗。
現(xiàn)在官方為我們提供了專有的集群方案: Redis Cluster,它非常優(yōu)雅地解決了Redis集群方面的問題。
2、數(shù)據(jù)分布理論
- 分布式數(shù)據(jù)庫首先要解決把整個數(shù)據(jù)集按照分區(qū)規(guī)則映射到多個節(jié)點的問題,即把數(shù)據(jù)集劃分到多個節(jié)點上,每個節(jié)點負(fù)責(zé)整體數(shù)據(jù)的一個子集。
- 常見的分區(qū)規(guī)則有哈希分區(qū)和順序分區(qū)兩種。
- Redis Cluster采用哈希分區(qū)規(guī)則。
| 哈希分區(qū) | - 離散度好 - 數(shù)據(jù)分布業(yè)務(wù)無關(guān) - 無法順序訪問 | Redis Cluster Cassandra Dynamo |
| 順序訪問 | - 離散度易傾斜 - 數(shù)據(jù)分布業(yè)務(wù)相關(guān) - 可順序訪問 | Bigtable HBase Hypertable |
常見的哈希分區(qū)規(guī)則有幾種:
1)節(jié)點取余分區(qū)
- 使用特定的數(shù)據(jù),如Redis的鍵或用戶ID,再根據(jù)節(jié)點數(shù)量N使用公式:hash(key)%N計算出哈希值,用來決定數(shù)據(jù)映射到哪一個節(jié)點上。
- 缺點:當(dāng)節(jié)點數(shù)量變化時,如擴(kuò)容或收縮節(jié)點,數(shù)據(jù)節(jié)點映射關(guān)系需要重新計算,會導(dǎo)致數(shù)據(jù)的重新遷移。
- 優(yōu)點:簡單性,常用于數(shù)據(jù)庫的分庫分表規(guī)則,一般采用預(yù)分區(qū)的方式,提前根據(jù)數(shù)據(jù)量規(guī)劃好分區(qū)數(shù),比如劃分為512或1024張表,保證可支撐未來一段時間的數(shù)據(jù)量,再根據(jù)負(fù)載情況將表遷移到其他數(shù)據(jù)庫中。
- 擴(kuò)容時通常采用翻倍擴(kuò)容,避免數(shù)據(jù)映射全部被打亂導(dǎo)致全量遷移的情況。
2)一致性哈希分區(qū)
- 一致性哈希分區(qū)(Distributed Hash Table)實現(xiàn)思路是為系統(tǒng)中每個節(jié)點分配一個token,范圍一般在0~2^32,這些token構(gòu)成一個哈希環(huán)。數(shù)據(jù)讀寫執(zhí)行節(jié)點查找操作時,先根據(jù)key計算hash值,然后順時針找到第一個大于等于該哈希值的token節(jié)點。
- 相比節(jié)點取余最大的好處在于加入和刪除節(jié)點只影響哈希環(huán)中相鄰的節(jié)點,對其他節(jié)點無影響。
- 一致性哈希分區(qū)存在幾個問題:
- 加減節(jié)點會造成哈希環(huán)中部分?jǐn)?shù)據(jù)無法命中,需要手動處理或者忽略這部分?jǐn)?shù)據(jù),因此一致性哈希常用于緩存場景。
- 當(dāng)使用少量節(jié)點時,節(jié)點變化將大范圍影響哈希環(huán)中數(shù)據(jù)映射,因此這種方式不適合少量數(shù)據(jù)節(jié)點的分布式方案。
- 普通的一致性哈希分區(qū)在增減節(jié)點時需要增加一倍或減去一半節(jié)點才能保證數(shù)據(jù)和負(fù)載的均衡。
- 正因為一致性哈希分區(qū)的這些缺點,一些分布式系統(tǒng)采用虛擬槽對一致性哈希進(jìn)行改進(jìn),比如Dynamo系統(tǒng)。
3)虛擬槽分區(qū)
- 虛擬槽分區(qū)巧妙地使用了哈??臻g,使用分散度良好的哈希函數(shù)把所有數(shù)據(jù)映射到一個固定范圍的整數(shù)集合中,整數(shù)定義為槽(slot)。這個范圍一般遠(yuǎn)遠(yuǎn)大于節(jié)點數(shù),比如Redis Cluster槽范圍是0~16383。
- 槽是集群內(nèi)數(shù)據(jù)管理和遷移的基本單位。采用大范圍槽的主要目的是為了方便數(shù)據(jù)拆分和集群擴(kuò)展。每個節(jié)點會負(fù)責(zé)一定數(shù)量的槽。
- Redis Cluster就是采用虛擬槽分區(qū)。
3、Redis數(shù)據(jù)分區(qū)
數(shù)據(jù)分區(qū)是分布式存儲的核心。
Redis Cluser采用虛擬槽分區(qū),所有的鍵根據(jù)哈希函數(shù)映射到0~16383整數(shù)槽內(nèi),計算公式:slot=CRC16(key)&16383。每一個節(jié)點負(fù)責(zé)維護(hù)一部分槽以及槽所映射的鍵值數(shù)據(jù)。
Redis虛擬槽分區(qū)的特點:
- 解耦數(shù)據(jù)和節(jié)點之間的關(guān)系,簡化了節(jié)點擴(kuò)容和收縮難度。
- 節(jié)點自身維護(hù)槽的映射關(guān)系,不需要客戶端或者代理服務(wù)維護(hù)槽分區(qū)元數(shù)據(jù)。
- 支持節(jié)點、槽、鍵之間的映射查詢,用于數(shù)據(jù)路由、在線伸縮等場景。
4、集群功能限制
Redis集群相對單機(jī)在功能上存在一些限制,限制如下:
- key批量操作支持有限。如mset、mget,目前只支持具有相同slot值的key執(zhí)行批量操作。對于映射為不同slot值的key由于執(zhí)行mget、mget等操作可能存在于多個節(jié)點上因此不被支持。
- key事務(wù)操作支持有限。同理只支持多key在同一節(jié)點上的事務(wù)操作,當(dāng)多個key分布在不同的節(jié)點上時無法使用事務(wù)功能。
- key作為數(shù)據(jù)分區(qū)的最小粒度,因此不能將一個大的鍵值對象如hash、list等映射到不同的節(jié)點。
- 不支持多數(shù)據(jù)庫空間。單機(jī)下的Redis可以支持16個數(shù)據(jù)庫,集群模式下只能使用一個數(shù)據(jù)庫空間,即db0。
- 復(fù)制結(jié)構(gòu)只支持一層,從節(jié)點只能復(fù)制主節(jié)點,不支持嵌套樹狀復(fù)制結(jié)構(gòu)。
5、搭建集群
搭建集群工作需要以下三個步驟:
- 準(zhǔn)備節(jié)點。
- 節(jié)點握手。
- 分配槽。
6、搭建集群步驟一:準(zhǔn)備節(jié)點
集群相關(guān)配置如下,其他配置和單機(jī)模式一致即可,配置文件命名規(guī)則redis-{port}.conf #節(jié)點端口 port 6379 # 開啟集群模式 cluster enabled yes # 節(jié)點超時時間,單位毫秒 cluster-node-timeout 15000 # 集群內(nèi)部配置文件 cluster config file "nodes 6379.conf"#cat data/nodes-6379.conf cfb28ef1deee4e0fa78da86abe5d24566744411e 127.0.0.1:6379 myself,master - 0 0 0 co vars currentEpoch 0 lastVoteEpoch 0127.0.0.1:6380> cluster nodes // 每個節(jié)點目前只能識別出自己的節(jié)點信息(要通過節(jié)點握手才能建立聯(lián)系)。 8e41673d59c9568aa9d29fb174ce733345b3e8f1 127.0.0.1:6380 myself,master - 0 0 0 co- Redis集群一般由多個節(jié)點組成,節(jié)點數(shù)量至少為6個才能保證組成完整高可用的集群。
- 每個節(jié)點需要開啟配置cluster-enabled yes,讓Redis運行在集群模式下。
- 建議為集群內(nèi)所有節(jié)點統(tǒng)一目錄,一般劃分三個目錄:conf、data、log,分別存放配置、數(shù)據(jù)和日志相關(guān)文件。把6個節(jié)點配置統(tǒng)一放在conf目錄下。
- 第一次啟動時如果沒有集群配置文件,它會自動創(chuàng)建一份,文件名稱采用cluster-config-file參數(shù)項控制,建議采用node-{port}.conf格式定義,通過使用端口號區(qū)分不同節(jié)點,防止同一機(jī)器下多個 節(jié)點彼此覆蓋,造成集群信息異常。
- 如果啟動時存在集群配置文件,節(jié)點會使用配置文件內(nèi)容初始化集群信息。
- 集群模式的Redis除了原有的配置文件之外又加了一份集群配置文件。當(dāng)集群內(nèi)節(jié)點信息發(fā)生變化,如添加節(jié)點、節(jié)點下線、故障轉(zhuǎn)移等。節(jié)點會自動保存集群狀態(tài)到配置文件中。
- 需要注意的是,Redis自動維護(hù)集群配置文件,不要手動修改,防止節(jié)點重啟時產(chǎn)生集群信息錯亂。
- 集群配置文件內(nèi)容記錄了集群初始狀態(tài),這里最重要的是節(jié)點ID,它是一個40位16進(jìn)制字符串,用于唯一標(biāo)識集群內(nèi)一個節(jié)點,之后很多集群操作都要借助于節(jié)點ID來完成。需要注意是,節(jié)點ID不同于運行ID。節(jié)點ID在集群初始化時只創(chuàng)建一次,節(jié)點重啟時會加載集群配置文件進(jìn)行重用,而Redis的運行ID每次重啟都會變化。
7、搭建集群步驟二:節(jié)點握手
- 節(jié)點握手是指一批運行在集群模式下的節(jié)點通過Gossip協(xié)議彼此通信,達(dá)到感知對方的過程。
- 節(jié)點握手是集群彼此通信的第一步,由客戶端發(fā)起命令:cluster meet {ip} {port}
- cluster meet命令是一個異步命令,執(zhí)行之后立刻返回。內(nèi)部發(fā)起與目標(biāo)節(jié)點進(jìn)行握手通信。
- cluster meet 127.0.0.1 6380讓節(jié)點6379和6380節(jié)點進(jìn)行握手通信:
- 節(jié)點6379本地創(chuàng)建6380節(jié)點信息對象,并發(fā)送meet消息。
- 節(jié)點6380接受到meet消息后,保存6379節(jié)點信息并回復(fù)pong消息。
- 之后節(jié)點6379和6380彼此定期通過ping/pong消息進(jìn)行正常的節(jié)點通信。
- 這里的meet、ping、pong消息是Gossip協(xié)議通信的載體,它的主要作用是節(jié)點彼此交換狀態(tài)數(shù)據(jù)信息。
- 對節(jié)點6379和6380分別執(zhí)行cluster nodes命令,可以看到它們彼此已經(jīng)感知到對方的存在。
- 我們只需要在集群內(nèi)任意節(jié)點上執(zhí)行cluster meet命令加入新節(jié)點,握手狀態(tài)會通過消息在集群內(nèi)傳播,這樣其他節(jié)點會自動發(fā)現(xiàn)新節(jié)點并發(fā)起握手 流程。
- 節(jié)點建立握手之后集群還不能正常工作,這時集群處于下線狀態(tài),所有的數(shù)據(jù)讀寫都被禁止。
- 通過cluster info命令可以獲取集群當(dāng)前狀態(tài)。
- 被分配的槽(cluster_slots_assigned)是0 ,由于目前所有的槽沒有分配到節(jié)點,因此集群無法完成槽到節(jié)點的映射。只有當(dāng)16384個槽全部分配給節(jié)點后,集群才進(jìn)入在線狀態(tài)。
8、搭建集群步驟三:分配槽
// 利用bash特性批量設(shè)置槽(slots),命令如下: redis-cli -h 127.0.0.1 p 6379 cluster addslots {0 . . .5461} redis-cli -h 127.0.0.1 -p 6380 cluster addslots {5462 . . .10922} redis-cli -h 127.0.0.1 -p 6381 cluster addslots {10923 . . .16383}// 執(zhí)行cluster info查看集群狀態(tài) 127.0.0.1 :6379> cluster info cluster_state:ok cluster_slots_assigned:16384 cluster_slots_ok:16384 cluster_slots_pfail:0 cluster_slots_fail:0 cluster_known_nodes:6 cluster_size:3 cluster_current_epoch:5 cluster_my_epoch:0 cluster_stats_messages_sent:4874 cluster_stats_messages_received:4726- Redis集群把所有的數(shù)據(jù)映射到16384個槽中。每個key會映射為一個固定的槽,只有當(dāng)節(jié)點分配了槽,才能響應(yīng)和這些槽關(guān)聯(lián)的鍵命令。
- 通過cluster addslots命令為節(jié)點分配槽。
- 當(dāng)前集群狀態(tài)是OK,集群進(jìn)入在線狀態(tài)。
- 所有的槽都已經(jīng)分配給節(jié)點,執(zhí)行cluster nodes命令可以看到節(jié)點和槽的分配關(guān)系。
- 作為一個完整的集群,每個負(fù)責(zé)處理槽的節(jié)點應(yīng)該具有從節(jié)點,保證當(dāng)它出現(xiàn)故障時可以自動進(jìn)行故障轉(zhuǎn)移。
- 集群模式下,Reids節(jié)點角色分為主節(jié)點和從節(jié)點。首次啟動的節(jié)點和被分配槽的節(jié)點都是主節(jié)點,從節(jié)點負(fù)責(zé)復(fù)制主節(jié)點槽信息和相關(guān)的數(shù)據(jù)。
- 使用cluster replicate {nodeId}命令讓一個節(jié)點成為從節(jié)點。其中命令執(zhí)行必須在對應(yīng)的從節(jié)點上執(zhí)行,nodeId是要復(fù)制主節(jié)點的節(jié)點ID。
- 通過cluster nodes命令查看集群狀態(tài)和復(fù)制關(guān)系。
- Redis官方提供了redis-trib.rb工具方便我們快速搭建集群。
9、用redis-trib.rb搭建集群
redis-trib.rb是采用Ruby實現(xiàn)的Redis集群管理工具。內(nèi)部通過Cluster相關(guān)命令幫我們簡化集群創(chuàng)建、檢查、槽遷移和均衡等常見運維操作,使用之前需要安裝Ruby依賴環(huán)境。
1)Ruby環(huán)境準(zhǔn)備
1、安裝Ruby: -- 下載ruby wget https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.1.tar.gz -- 安裝ruby tar xvf ruby-2.3.1.tar.gz ./configure -prefix=/usr/local/ruby make make install cd /usr/local/ruby sudo cp bin/ruby /usr/local/bin sudo cp bin/gem /usr/local/bin2、安裝rubygem redis依賴: wget http://rubygems.org/downloads/redis-3.3.0.gem gem install -l redis-3.3.0.gem gem list --check redis gem3、安裝redis-trib.rb: sudo cp /{redis_home}/src/redis-trib.rb /usr/local/bin4、執(zhí)行redis-trib.rb命令確認(rèn)環(huán)境是否正確: # redis-trib.rbUsage: redis-trib <command> <options> <arguments . . .> create host1:port1 . . . hostN:portN --replicas <arg> check host:port info host:port fix host:port --timeout <arg> reshard host:port --from <arg> --to <arg> --slots <arg> --yes --timeout <arg> --pipeline <arg> . . .忽略 . . .- 從redis-trib.rb的提示信息可以看出,它提供了集群創(chuàng)建、檢查、修復(fù)、均衡等命令行工具。
- 使用redis-trib.rb create命令可快速搭建集群。
2)準(zhǔn)備節(jié)點
// 準(zhǔn)備好節(jié)點配置并啟動: redis-server conf/redis-6481.conf redis-server conf/redis-6482.conf redis-server conf/redis-6483.conf redis-server conf/redis-6484.conf redis-server conf/redis-6485.conf redis-server conf/redis-6486.conf3)創(chuàng)建集群
// 使用redis-trib.rb create命令完成節(jié)點握手和槽分配過程,命令如下: redis-trib.rb create --replicas 1 127.0.0.1:6481 127.0.0.1:6482 127.0.0.1:6483 127.0.0.1:6484 127.0.0.1:6485 127.0.0.1:6486- –replicas參數(shù)指定集群中每個主節(jié)點配備幾個從節(jié)點,這里設(shè)置為1。
- 如果部署節(jié)點使用不同的IP地址,redis-trib.rb會盡可能保證主從節(jié)點不分配在同一機(jī)器下。
- 節(jié)點列表順序用于確定主從角色,先主節(jié)點之后是從節(jié)點。
- 需要注意給redis-trib.rb的節(jié)點地址必須是不包含任何槽/數(shù)據(jù)的節(jié)點,否則會拒絕創(chuàng)建集群。
4)集群完整性檢查
redis-trib.rb check 127.0.0.1:6481- 集群完整性指所有的槽都分配到存活的主節(jié)點上,只要16384個槽中有一個沒有分配給節(jié)點則表示集群不完整。
- 可以使用redis-trib.rb check命令檢測創(chuàng)建的集群是否成功,check命令只需要給出集群中任意一個節(jié)點地址就可以完成整個集群的檢查工作。
10、節(jié)點通信流程
- 在分布式存儲中需要提供維護(hù)節(jié)點元數(shù)據(jù)信息的機(jī)制,所謂元數(shù)據(jù)是指:節(jié)點負(fù)責(zé)哪些數(shù)據(jù),是否出現(xiàn)故障等狀態(tài)信息。
- 常見的元數(shù)據(jù)維護(hù)方式分為:集中式和P2P方式。Redis集群采用P2P的Gossip(流言)協(xié)議,Gossip協(xié)議工作原理就是節(jié)點彼此不斷通信交換信息,一段時間后所有的節(jié)點都會知道集群完整的信息。
- 通信過程說明:
- 集群中的每個節(jié)點都會單獨開辟一個TCP通道,用于節(jié)點之間彼此通信,通信端口號在基礎(chǔ)端口上加10000。
- 每個節(jié)點在固定周期內(nèi)通過特定規(guī)則選擇幾個節(jié)點發(fā)送ping消息。
- 接收到ping消息的節(jié)點用pong消息作為響應(yīng)。
- 集群中每個節(jié)點通過一定規(guī)則挑選要通信的節(jié)點,每個節(jié)點可能知道全部節(jié)點,也可能僅知道部分節(jié)點,只要這些節(jié)點彼此可以正常通信,最終它們會達(dá)到一致的狀態(tài)。當(dāng)節(jié)點出故障、新節(jié)點加入、主從角色變化、槽信息變更等事件發(fā)生時,通過不斷的ping/pong消息通信,經(jīng)過一段時間后所有的節(jié)點都會知道整個集群全部節(jié)點的最新狀態(tài),從而達(dá)到集群狀態(tài)同步的目的。
11、Gossip消息
- Gossip協(xié)議的主要職責(zé)就是信息交換。信息交換的載體就是節(jié)點彼此發(fā)送的Gossip消息。
- 常用的Gossip消息可分為: ping消息、pong消息、meet消息、fail消息等。
- meet消息:用于通知新節(jié)點加入。消息發(fā)送者通知接收者加入到當(dāng)前集群,meet消息通信正常完成后,接收節(jié)點會加入到集群中并進(jìn)行周期性的ping、pong消息交換。
- ping消息:集群內(nèi)交換最頻繁的消息,集群內(nèi)每個節(jié)點每秒向多個其他節(jié)點發(fā)送ping消息,用于檢測節(jié)點是否在線和交換彼此狀態(tài)信息。ping消息發(fā)送封裝了自身節(jié)點和部分其他節(jié)點的狀態(tài)數(shù)據(jù)。
- pong消息:當(dāng)接收到ping、meet消息時,作為響應(yīng)消息回復(fù)給發(fā)送方確認(rèn)消息正常通信。pong消息內(nèi)部封裝了自身狀態(tài)數(shù)據(jù)。節(jié)點也可以向集群內(nèi)廣播自身的pong消息來通知整個集群對自身狀態(tài)進(jìn)行更新。
- fail消息:當(dāng)節(jié)點判定集群內(nèi)另一個節(jié)點下線時,會向集群內(nèi)廣播一個fail消息,其他節(jié)點接收到fail消息之后把對應(yīng)節(jié)點更新為下線狀態(tài)。
- 所有的消息格式劃分為:消息頭和消息體。消息頭包含發(fā)送節(jié)點自身狀態(tài)數(shù)據(jù),接收節(jié)點根據(jù)消息頭就可以獲取到發(fā)送節(jié)點的相關(guān)數(shù)據(jù)。
- 集群內(nèi)所有的消息都采用相同的消息頭結(jié)構(gòu)clusterMsg,它包含了發(fā)送節(jié)點關(guān)鍵信息,如節(jié)點id 、槽映射、節(jié)點標(biāo)識(主從角色,是否下線)等。消息體在Redis內(nèi)部采用clusterMsgData結(jié)構(gòu)聲明。
- 消息體clusterMsgData定義發(fā)送消息的數(shù)據(jù),其中ping、meet、pong都采用cluster MsgDataGossip數(shù)組作為消息體數(shù)據(jù),實際消息類型使用消息頭的type屬性區(qū)分。每個消息體包含該節(jié)點的多個clusterMsgDataGossip結(jié)構(gòu)數(shù)據(jù),用于信息交換。
- 當(dāng)接收到ping、meet消息時,接收節(jié)點會解析消息內(nèi)容并根據(jù)自身的識別情況做出相應(yīng)處理。
12、節(jié)點選擇
- Redis集群內(nèi)節(jié)點通信采用固定頻率(定時任務(wù)每秒執(zhí)行10次)。因此節(jié)點每次選擇需要通信的節(jié)點列表變得非常重要。通信節(jié)點選擇過多雖然可以做到信息及時交換但成本過高。節(jié)點選擇過少會降低集群內(nèi)所有節(jié)點彼此信息交換頻率,從而影響故障判定、新節(jié)點發(fā)現(xiàn)等需求的速度。因此Redis集群的Gossip協(xié)議需要兼顧信息交換實時性和成本開銷。
- 消息交換的成本主要體現(xiàn)在單位時間選擇發(fā)送消息的節(jié)點數(shù)量和每個消息攜帶的數(shù)據(jù)量。
1)選擇發(fā)送消息的節(jié)點數(shù)量
- 集群內(nèi)每個節(jié)點維護(hù)定時任務(wù)默認(rèn)每秒執(zhí)行10次,每秒會隨機(jī)選取5個節(jié)點找出最久沒有通信的節(jié)點發(fā)送ping消息,用于保證Gossip信息交換的隨機(jī)性。
- 每100毫秒都會掃描本地節(jié)點列表,如果發(fā)現(xiàn)節(jié)點最近一次接受pong消息的時間大于cluster_node_timeout/2 ,則立刻發(fā)送ping消息,防止該節(jié)點信息太長時間未更新。
- 根據(jù)以上規(guī)則得出每個節(jié)點每秒需要發(fā)送ping消息的數(shù)量= 1+10*num(node.pong_received>cluster_node_timeout/2),因此cluster_node_timeout參數(shù)對消息發(fā)送的節(jié)點數(shù)量影響非常大。當(dāng)我們的帶寬資源緊張時,可以適當(dāng)調(diào)大這個參數(shù),如從默認(rèn)15秒改為30秒來降低帶寬占用率。過度調(diào)大cluster_node_timeout會影響消息交換的頻率從而影響故障轉(zhuǎn)移、槽信息更新、新節(jié)點發(fā)現(xiàn)的速度。因此需要根據(jù)業(yè)務(wù)容忍度和資源消耗進(jìn)行平衡。
- 同時整個集群消息總交換量也跟節(jié)點數(shù)成正比。
2)消息數(shù)據(jù)量
- 每個ping消息的數(shù)據(jù)量體現(xiàn)在消息頭和消息體中,其中消息頭主要占用空間的字段是myslots[CLUSTER_SLOTS/8],占用2KB,這塊空間占用相對固定。消息體會攜帶一定數(shù)量的其他節(jié)點信息用于信息交換。
- 消息體攜帶數(shù)據(jù)量跟集群的節(jié)點數(shù)息息相關(guān),更大的集群每次消息通信的成本也就更高。
13、集群伸縮原理
- Redis集群可以實現(xiàn)對節(jié)點的靈活上下線控制。其中原理可抽象為槽和對應(yīng)數(shù)據(jù)在不同節(jié)點之間靈活移動。
- 如果希望加入1個節(jié)點實現(xiàn)集群擴(kuò)容時,需要通過相關(guān)命令把一部分槽和數(shù)據(jù)遷移給新節(jié)點,因此每個節(jié)點負(fù)責(zé)的槽和數(shù)據(jù)相比之前變少了,從而達(dá)到了集群擴(kuò)容的目的。
14、擴(kuò)容集群
1)準(zhǔn)備新節(jié)點
- 需要提前準(zhǔn)備好新節(jié)點并運行在集群模式下,新節(jié)點建議跟集群內(nèi)的節(jié)點配置保持一致,便于管理統(tǒng)一。
2)加入集群
- 新節(jié)點依然采用cluster meet命令加入到現(xiàn)有集群中。在集群內(nèi)任意節(jié)點執(zhí)行cluster meet命令讓6385和6386節(jié)點加入進(jìn)來。
- 集群內(nèi)新舊節(jié)點經(jīng)過一段時間的ping/pong消息通信之后,所有節(jié)點會發(fā)現(xiàn)新節(jié)點并將它們的狀態(tài)保存到本地。
- 新節(jié)點剛開始都是主節(jié)點狀態(tài),但是由于沒有負(fù)責(zé)的槽,所以不能接受任何讀寫操作。對于新節(jié)點的后續(xù)操作我們一般有兩種選擇:
- 為它遷移槽和數(shù)據(jù)實現(xiàn)擴(kuò)容。
- 作為其他主節(jié)點的從節(jié)點負(fù)責(zé)故障轉(zhuǎn)移。
- redis-trib.rb工具也實現(xiàn)了為現(xiàn)有集群添加新節(jié)點的命令,還實現(xiàn)了直接添加為從節(jié)點的支持,命令見下方。內(nèi)部同樣采用cluster meet命令實現(xiàn)加入集群功能。
- 正式環(huán)境建議使用redis-trib.rb add-node命令加入新節(jié)點,該命令內(nèi)部會執(zhí)行新節(jié)點狀態(tài)檢查,如果新節(jié)點已經(jīng)加入其他集群或者包含數(shù)據(jù),則放棄集群加入操作并打印相關(guān)信息。如果我們手動執(zhí)行cluster meet命令加入已經(jīng)存在于其他集群的節(jié)點,會造成被加入節(jié)點的集群合并到現(xiàn)有集群的情況,從而造成數(shù)據(jù)丟失和錯亂,后果非常嚴(yán)重,線上謹(jǐn)慎操作。
3)遷移槽和數(shù)據(jù)
- 加入集群后需要為新節(jié)點遷移槽和相關(guān)數(shù)據(jù),槽在遷移過程中集群可以正常提供讀寫服務(wù),遷移過程是集群擴(kuò)容最核心的環(huán)節(jié)。
- 槽遷移計劃:槽是Redis集群管理數(shù)據(jù)的基本單位,首先需要為新節(jié)點制定槽的遷移計劃,確定原有節(jié)點的哪些槽需要遷移到新節(jié)點。遷移計劃需要確保每個節(jié)點負(fù)責(zé)相似數(shù)量的槽,從而保證各節(jié)點的數(shù)據(jù)均勻。
- 遷移數(shù)據(jù):數(shù)據(jù)遷移過程是逐個槽進(jìn)行的,每個槽數(shù)據(jù)遷移的流程說明如下:
- 對目標(biāo)節(jié)點發(fā)送cluster setslot {slot} importing {sourceNodeId}命令,讓目標(biāo)節(jié)點準(zhǔn)備導(dǎo)入槽的數(shù)據(jù)。
- 對源節(jié)點發(fā)送cluster setslot {slot} migrating {targetNodeId}命令,讓源節(jié)點準(zhǔn)備遷出槽的數(shù)據(jù)。
- 源節(jié)點循環(huán)執(zhí)行cluster getkeysinslot {slot} {count}命令,獲取count個屬于槽{slot}的鍵。
- 在源節(jié)點上執(zhí)行migrate {targetIp}{targetPort}""0{timeout}keys {keys…}命令,把獲取的鍵通過流水線(pipeline)機(jī)制批量遷移到目標(biāo)節(jié)點,批量遷移版本的migrate命令在Redis3.0.6以上版本提供,之前的migrate命令只能單個鍵遷移。對于大量key的場景,批量鍵遷移將極大降低節(jié)點之間網(wǎng)絡(luò)IO次數(shù)。
- 重復(fù)執(zhí)行步驟3)和步驟4)直到槽下所有的鍵值數(shù)據(jù)遷移到目標(biāo)節(jié)點。
- 向集群內(nèi)所有主節(jié)點發(fā)送cluster setslot {slot} node {targetNodeId}命令,通知槽分配給目標(biāo)節(jié)點。為了保證槽節(jié)點映射變更及時傳播,需要遍歷發(fā)送給所有主節(jié)點更新被遷移的槽指向新節(jié)點。
- 添加從節(jié)點:使用cluster replicate {masterNodeId}命令為主節(jié)點添加對應(yīng)從節(jié)點,注意在集群模式下slaveof添加從節(jié)點操作不再支持。從節(jié)點內(nèi)部除了對主節(jié)點發(fā)起全量復(fù)制之外,還需要更新本地節(jié)點的集群相關(guān)狀態(tài)。
實際操作槽遷移過程時肯定涉及大量槽并且每個槽對應(yīng)非常多的鍵。因此redis-trib提供了槽重分片功能,命令如下:
redis-trib.rb reshard host:port --from <arg> --to <arg> --slots <arg> --yes --t <arg> --pipeline <arg>
參數(shù)說明:
- host:port:必傳參數(shù),集群內(nèi)任意節(jié)點地址,用來獲取整個集群信息。
- –from:制定源節(jié)點的id,如果有多個源節(jié)點,使用逗號分隔,如果是all源節(jié)點變?yōu)榧簝?nèi)所有主節(jié)點,在遷移過程中提示用戶輸入。
- –to:需要遷移的目標(biāo)節(jié)點的id,目標(biāo)節(jié)點只能填寫一個,在遷移過程中提示用戶輸入。
- –slots:需要遷移槽的總數(shù)量,在遷移過程中提示用戶輸入。
- –yes:當(dāng)打印出reshard執(zhí)行計劃時,是否需要用戶輸入yes確認(rèn)后再執(zhí)行reshard。
- –timeout:控制每次migrate操作的超時時間,默認(rèn)為60000毫秒。
- –pipeline:控制每次批量遷移鍵的數(shù)量,默認(rèn)為10。
reshard命令簡化了數(shù)據(jù)遷移的工作量,其內(nèi)部針對每個槽的數(shù)據(jù)遷移同樣使用之前的流程。
由于槽用于hash運算本身順序沒有意義,因此無須強(qiáng)制要求節(jié)點負(fù)責(zé)槽的順序性。遷移之后建議使用redis-trib.rb rebalance命令檢查節(jié)點之間槽的均衡性。
15、收縮集群
安全下線節(jié)點的流程說明:
- 首先需要確定下線節(jié)點是否有負(fù)責(zé)的槽,如果是,需要把槽遷移到其他節(jié)點,保證節(jié)點下線后整個集群槽節(jié)點映射的完整性。
- 當(dāng)下線節(jié)點不再負(fù)責(zé)槽或者本身是從節(jié)點時,就可以通知集群內(nèi)其他節(jié)點忘記下線節(jié)點,當(dāng)所有的節(jié)點忘記該節(jié)點后可以正常關(guān)閉。
1)下線遷移槽
- 下線節(jié)點需要把自己負(fù)責(zé)的槽遷移到其他節(jié)點,原理與之前節(jié)點擴(kuò)容的遷移槽過程一致。
- 收縮正好和擴(kuò)容遷移方向相反。
- 下線節(jié)點槽遷出完成后,剩下的步驟需要讓集群忘記該節(jié)點。
2)忘記節(jié)點
- 由于集群內(nèi)的節(jié)點不停地通過Gossip消息彼此交換節(jié)點狀態(tài),因此需要通過一種健壯的機(jī)制讓集群內(nèi)所有節(jié)點忘記下線的節(jié)點。也就是說讓其他節(jié)點不再與要下線節(jié)點進(jìn)行Gossip消息交換。Redis提供了cluster forget{downNodeId}命令實現(xiàn)該功能。
- 當(dāng)節(jié)點接收到cluster forget{down NodeId}命令后,會把nodeId指定的節(jié)點加入到禁用列表中,在禁用列表內(nèi)的節(jié)點不再發(fā)送Gossip消息。
- 禁用列表有效期是60秒,超過60秒節(jié)點會再次參與消息交換。也就是說當(dāng)?shù)谝淮蝔orget命令發(fā)出后,我們有60秒的時間讓集群內(nèi)的所有節(jié)點忘記下線節(jié)點。
- 線上操作不建議直接使用cluster forget命令下線節(jié)點,需要跟大量節(jié)點命令交互,實際操作起來過于繁瑣并且容易遺漏forget節(jié)點。建議使用redis-trib.rb del-node {host:port}{downNodeId}命令。
- 當(dāng)下線主節(jié)點具有從節(jié)點時需要把該從節(jié)點指向到其他主節(jié)點,因此對于主從節(jié)點都下線的情況,建議先下線從節(jié)點再下線主節(jié)點,防止不必要的全量復(fù)制。
16、請求路由——請求重定向
- Redis集群對客戶端通信協(xié)議做了比較大的修改,為了追求性能最大化,并沒有采用代理的方式而是采用客戶端直連節(jié)點的方式。因此對于希望從單機(jī)切換到集群環(huán)境的應(yīng)用需要修改客戶端代碼。
- 在集群模式下,Redis接收任何鍵相關(guān)命令時首先計算鍵對應(yīng)的槽,再根據(jù)槽找出所對應(yīng)的節(jié)點,如果節(jié)點是自身,則處理鍵命令;否則回復(fù)MOVED重定向錯誤,通知客戶端請求正確的節(jié)點。這個過程稱為MOVED重定向。
- 可以借助cluster keyslot{key}命令返回key所對應(yīng)的槽。
- 重定向信息包含了鍵所對應(yīng)的槽以及負(fù)責(zé)該槽的節(jié)點地址,根據(jù)這些信息客戶端就可以向正確的節(jié)點發(fā)起請求。
- 使用redis-cli命令時,可以加入-c參數(shù)支持自動重定向,簡化手動發(fā)起重定向操作。
- redis-cli自動幫我們連接到正確的節(jié)點執(zhí)行命令,這個過程是在redis-cli內(nèi)部維護(hù),實質(zhì)上是client端接到MOVED信息之后再次發(fā)起請求,并不在Redis節(jié)點中完成請求轉(zhuǎn)發(fā)。
- 節(jié)點對于不屬于它的鍵命令只回復(fù)重定向響應(yīng),并不負(fù)責(zé)轉(zhuǎn)發(fā)。
- 正因為集群模式下把解析發(fā)起重定向的過程放到客戶端完成,所以集群客戶端協(xié)議相對于單機(jī)有了很大的變化。
- 鍵命令執(zhí)行步驟主要分兩步:計算槽,查找槽所對應(yīng)的節(jié)點。
1)計算槽
- Redis首先需要計算鍵所對應(yīng)的槽。根據(jù)鍵的有效部分使用CRC16函數(shù)計算出散列值,再取對16383的余數(shù),使每個鍵都可以映射到0~16383槽范圍內(nèi)。
- 如果鍵內(nèi)容包含{和}大括號字符,則計算槽的有效部分是括號內(nèi)的內(nèi)容;否則采用鍵的全內(nèi)容計算槽。其中鍵內(nèi)部使用大括號包含的內(nèi)容又叫做hash_tag,它提供不同的鍵可以具備相同slot的功能,常用于Redis IO優(yōu)化。例如在集群模式下使用mget等命令優(yōu)化批量調(diào)用時,鍵列表必須具有相同的slot,否則會報錯。這時可以利用hash_tag讓不同的鍵具有相同的slot達(dá)到優(yōu)化的目的。
- Pipeline同樣可以受益于hash_tag,由于Pipeline只能向一個節(jié)點批量發(fā)送執(zhí)行命令,而相同slot必然會對應(yīng)到唯一的節(jié)點,降低了集群使用Pipeline的門檻。
2)槽節(jié)點查找
- Redis計算得到鍵對應(yīng)的槽后,需要查找槽所對應(yīng)的節(jié)點。集群內(nèi)通過消息交換每個節(jié)點都會知道所有節(jié)點的槽信息,內(nèi)部保存在clusterState結(jié)構(gòu)中。
- 根據(jù)MOVED重定向機(jī)制,客戶端可以隨機(jī)連接集群內(nèi)任一Redis獲取鍵所在節(jié)點,這種客戶端又叫Dummy(傀儡)客戶端,它優(yōu)點是代碼實現(xiàn)簡單,對客戶端協(xié)議影響較小,只需要根據(jù)重定向信息再次發(fā)送請求即可。但是它的弊端很明顯,每次執(zhí)行鍵命令前都要到Redis上進(jìn)行重定向才能找到要執(zhí)行命令的節(jié)點,額外增加了IO開銷,這不是Redis集群高效的使用方式。正因為如此通常集群客戶端都采用另一種實現(xiàn):Smart(智能)客戶端。
17、請求路由——Smart客戶端
1)Smart客戶端原理
- 大多數(shù)開發(fā)語言的Redis客戶端都采用Smart客戶端支持集群協(xié)議。
- Smart客戶端通過在內(nèi)部維護(hù)slot→node的映射關(guān)系,本地就可實現(xiàn)鍵到節(jié)點的查找,從而保證IO效率的最大化,而MOVED重定向負(fù)責(zé)協(xié)助Smart客戶端更新slot→node映射。
- Smart客戶端操作集群的流程:略。
2)Smart客戶端——JedisCluster
- Redis Cluster雖然提供了分布式的特性,但是有些命令或者操作,諸如keys、flushall、刪除指定模式的鍵,需要遍歷所有節(jié)點才可以完成。
- Redis Cluster中,由于key分布到各個節(jié)點上,會造成無法實現(xiàn)mget、mset等功能。但是可以利用CRC16算法計算出key對應(yīng)的slot,以及Smart客戶端保存了slot和節(jié)點對應(yīng)關(guān)系的特性,將屬于同一個Redis節(jié)點的key進(jìn)行歸檔,然后分別對每個節(jié)點對應(yīng)的子key列表執(zhí)行mget或者pipeline操作。
- Lua和事務(wù)需要所操作的key,必須在一個節(jié)點上,不過Redis Cluster提供了hashtag,如果開發(fā)人員確實要使用Lua或者事務(wù),可以將所要操作的key使用一個hashtag。
18、請求路由——ASK重定向
1)客戶端ASK重定向流程
- Redis集群支持在線遷移槽(slot)和數(shù)據(jù)來完成水平伸縮,當(dāng)slot對應(yīng)的數(shù)據(jù)從源節(jié)點到目標(biāo)節(jié)點遷移過程中,客戶端需要做到智能識別,保證鍵命令可正常執(zhí)行。
- 當(dāng)一個slot數(shù)據(jù)從源節(jié)點遷移到目標(biāo)節(jié)點時,期間可能出現(xiàn)一部分?jǐn)?shù)據(jù)在源節(jié)點,而另一部分在目標(biāo)節(jié)點的情況時,客戶端鍵命令執(zhí)行流程將發(fā)生變化:
- 客戶端根據(jù)本地slots緩存發(fā)送命令到源節(jié)點,如果存在鍵對象則直接執(zhí)行并返回結(jié)果給客戶端。
- 如果鍵對象不存在,則可能存在于目標(biāo)節(jié)點,這時源節(jié)點會回復(fù)ASK重定向異常。格式如下:(error)ASK{slot}{targetIP} :{targetPort}。
- 客戶端從ASK重定向異常提取出目標(biāo)節(jié)點信息,發(fā)送asking命令到目標(biāo)節(jié)點打開客戶端連接標(biāo)識,再執(zhí)行鍵命令。如果存在則執(zhí)行,不存在則返回不存在信息。
- ASK與MOVED雖然都是對客戶端的重定向控制,但是有著本質(zhì)區(qū)別。ASK重定向說明集群正在進(jìn)行slot數(shù)據(jù)遷移,客戶端無法知道什么時候遷移完成,因此只能是臨時性的重定向,客戶端不會更新slots緩存。但是MOVED重定向說明鍵對應(yīng)的槽已經(jīng)明確指定到新的節(jié)點,因此需要更新slots緩存。
2)節(jié)點內(nèi)部處理
- 為了支持ASK重定向,源節(jié)點和目標(biāo)節(jié)點在內(nèi)部的clusterState結(jié)構(gòu)中維護(hù)當(dāng)前正在遷移的槽信息,用于識別槽遷移情況。
- 節(jié)點每次接收到鍵命令時,都會根據(jù)clusterState內(nèi)的遷移屬性進(jìn)行命令處理,如下所示:
- 如果鍵所在的槽由當(dāng)前節(jié)點負(fù)責(zé),但鍵不存在則查找migrating_slots_to數(shù)組查看槽是否正在遷出,如果是返回ASK重定向。
- 如果客戶端發(fā)送asking命令打開了CLIENT_ASKING標(biāo)識,則該客戶端下次發(fā)送鍵命令時查找importing_slots_from數(shù)組獲取clusterNode ,如果指向自身則執(zhí)行命令。
- 需要注意的是,asking命令是一次性命令,每次執(zhí)行完后客戶端標(biāo)識都會修改回原狀態(tài),因此每次客戶端接收到ASK重定向后都需要發(fā)送asking命令。
- 批量操作。ASK重定向?qū)捂I命令支持得很完善,但是,在開發(fā)中我們經(jīng)常使用批量操作,如mget或pipeline。當(dāng)槽處于遷移狀態(tài)時,批量操作會受到影響。
- 當(dāng)在集群環(huán)境下使用mget、mset等批量操作時,slot遷移數(shù)據(jù)期間由于鍵列表無法保證在同一節(jié)點,會導(dǎo)致大量錯誤。
- 使用smart客戶端批量操作集群時,需要評估m(xù)get/mset、Pipeline等方式在slot遷移場景下的容錯性,防止集群遷移造成大量錯誤和數(shù)據(jù)丟失的情況。
- 集群環(huán)境下對于使用批量操作的場景,建議優(yōu)先使用Pipeline方式,在客戶端實現(xiàn)對ASK重定向的正確處理,這樣既可以受益于批量操作的IO優(yōu)化,又可以兼容slot遷移場景。
19、故障轉(zhuǎn)移之故障發(fā)現(xiàn)
- Redis集群內(nèi)節(jié)點通過ping/pong消息實現(xiàn)節(jié)點通信,消息不但可以傳播節(jié)點槽信息,還可以傳播其他狀態(tài)如:主從狀態(tài)、節(jié)點故障等。因此故障發(fā)現(xiàn)也是通過消息傳播機(jī)制實現(xiàn)的,主要環(huán)節(jié)包括:主觀下線(pfail)和客觀下線(fail)。
- 主觀下線:指某個節(jié)點認(rèn)為另一個節(jié)點不可用,即下線狀態(tài),這個狀態(tài)并不是最終的故障判定,只能代表一個節(jié)點的意見,可能存在誤判情況。
- 客觀下線:指標(biāo)記一個節(jié)點真正的下線,集群內(nèi)多個節(jié)點都認(rèn)為該節(jié)點不可用,從而達(dá)成共識的結(jié)果。如果是持有槽的主節(jié)點故障,需要為該節(jié)點進(jìn)行故障轉(zhuǎn)移。
- 主觀下線:集群中每個節(jié)點都會定期向其他節(jié)點發(fā)送ping消息,接收節(jié)點回復(fù)pong消息作為響應(yīng)。如果在cluster-node-timeout時間內(nèi)通信一直失敗,則發(fā)送節(jié)點會認(rèn)為接收節(jié)點存在故障,把接收節(jié)點標(biāo)記為主觀下線(pfail)狀態(tài)。流程說明如下:
- 節(jié)點a發(fā)送ping消息給節(jié)點b,如果通信正常將接收到pong消息,節(jié)點a更新最近一次與節(jié)點b的通信時間。
- 如果節(jié)點a與節(jié)點b通信出現(xiàn)問題則斷開連接,下次會進(jìn)行重連。如果一直通信失敗,則節(jié)點a記錄的與節(jié)點b最后通信時間將無法更新。
- 節(jié)點a內(nèi)的定時任務(wù)檢測到與節(jié)點b最后通信時間超高cluster-node-timeout時,更新本地對節(jié)點b的狀態(tài)為主觀下線(pfail)。
- 主觀下線簡單來講就是,當(dāng)cluster-note-timeout時間內(nèi)某節(jié)點無法與另一個節(jié)點順利完成ping消息通信時,則將該節(jié)點標(biāo)記為主觀下線狀態(tài)。每個節(jié)點內(nèi)的cluster State結(jié)構(gòu)都需要保存其他節(jié)點信息,用于從自身視角判斷其他節(jié)點的狀態(tài)。結(jié)構(gòu)關(guān)鍵屬性中最重要的屬性是flags,用于標(biāo)示該節(jié)點對應(yīng)狀態(tài)。
- 當(dāng)某個節(jié)點判斷另一個節(jié)點主觀下線后,相應(yīng)的節(jié)點狀態(tài)會跟隨消息在集群內(nèi)傳播。ping/pong消息的消息體會攜帶集群1/10的其他節(jié)點狀態(tài)數(shù)據(jù),當(dāng)接受節(jié)點發(fā)現(xiàn)消息體中含有主觀下線的節(jié)點狀態(tài)時,會在本地找到故障節(jié)點的ClusterNode結(jié)構(gòu),保存到下線報告鏈表中。通過Gossip消息傳播,集群內(nèi)節(jié)點不斷收集到故障節(jié)點的下線報告。當(dāng)半數(shù)以上持有槽的主節(jié)點都標(biāo)記某個節(jié)點是主觀下線時,觸發(fā)客觀下線流程。流程說明如下:
- 當(dāng)消息體內(nèi)含有其他節(jié)點的pfail狀態(tài)會判斷發(fā)送節(jié)點的狀態(tài),如果發(fā)送節(jié)點是主節(jié)點則對報告的pfail狀態(tài)處理,從節(jié)點則忽略。
- 找到pfail對應(yīng)的節(jié)點結(jié)構(gòu),更新clusterNode內(nèi)部下線報告鏈表。
- 根據(jù)更新后的下線報告鏈表告嘗試進(jìn)行客觀下線。
- 為什么必須是負(fù)責(zé)槽的主節(jié)點參與故障發(fā)現(xiàn)決策?因為集群模式下只有處理槽的主節(jié)點才負(fù)責(zé)讀寫請求和集群槽等關(guān)鍵信息維護(hù),而從節(jié)點只進(jìn)行主節(jié)點數(shù)據(jù)和狀態(tài)信息的復(fù)制。
- 為什么半數(shù)以上處理槽的主節(jié)點?必須半數(shù)以上是為了應(yīng)對網(wǎng)絡(luò)分區(qū)等原因造成的集群分割情況,被分割的小集群因為無法完成從主觀下線到客觀下線這一關(guān)鍵過程,從而防止小集群完成故障轉(zhuǎn)移之后繼續(xù)對外提供服務(wù)。
- 每個節(jié)點ClusterNode結(jié)構(gòu)中都會存在一個下線鏈表結(jié)構(gòu),保存了其他主節(jié)點針對當(dāng)前節(jié)點的下線報告。下線報告中保存了報告故障的節(jié)點結(jié)構(gòu)和最近收到下線報告的時間,當(dāng)接收到fail狀態(tài)時,會維護(hù)對應(yīng)節(jié)點的下線上報鏈表。每個下線報告都存在有效期,每次在嘗試觸發(fā)客觀下線時,都會檢測下線報告是否過期,對于過期的下線報告將被刪除。如果在cluster-node-time2的時間內(nèi)該下線報告沒有得到更新則過期并刪除。下線報告的有效期限是server.cluster_node_timeout2 ,主要是針對故障誤報的情況。
- 如果在cluster-node-time*2時間內(nèi)無法收集到一半以上槽節(jié)點的下線報告,那么之前的下線報告將會過期,也就是說主觀下線上報的速度追趕不上下線報告過期的速度,那么故障節(jié)點將永遠(yuǎn)無法被標(biāo)記為客觀下線從而導(dǎo)致故障轉(zhuǎn)移失敗。因此不建議將cluster-node-time設(shè)置得過小。
- 集群中的節(jié)點每次接收到其他節(jié)點的pfail狀態(tài),都會嘗試觸發(fā)客觀下線,流程說明如下:
- 首先統(tǒng)計有效的下線報告數(shù)量,如果小于集群內(nèi)持有槽的主節(jié)點總數(shù)的一半則退出。
- 當(dāng)下線報告大于槽主節(jié)點數(shù)量一半時,標(biāo)記對應(yīng)故障節(jié)點為客觀下線狀態(tài)。
- 向集群廣播一條fail消息,通知所有的節(jié)點將故障節(jié)點標(biāo)記為客觀下線,fail消息的消息體只包含故障節(jié)點的ID。
- 廣播fail消息是客觀下線的最后一步,它承擔(dān)著非常重要的職責(zé):
- 通知集群內(nèi)所有的節(jié)點標(biāo)記故障節(jié)點為客觀下線狀態(tài)并立刻生效。
- 通知故障節(jié)點的從節(jié)點觸發(fā)故障轉(zhuǎn)移流程。
- 網(wǎng)絡(luò)分區(qū)會導(dǎo)致分割后的小集群無法收到大集群的fail消息,因此如果故障節(jié)點所有的從節(jié)點都在小集群內(nèi)將導(dǎo)致無法完成后續(xù)故障轉(zhuǎn)移,因此部署主從結(jié)構(gòu)時需要根據(jù)自身機(jī)房/機(jī)架拓?fù)浣Y(jié)構(gòu),降低主從被分區(qū)的可能性。
20、故障轉(zhuǎn)移之故障恢復(fù)
- 故障節(jié)點變?yōu)榭陀^下線后,如果下線節(jié)點是持有槽的主節(jié)點則需要在它的從節(jié)點中選出一個替換它,從而保證集群的高可用。下線主節(jié)點的所有從 節(jié)點承擔(dān)故障恢復(fù)的義務(wù),當(dāng)從節(jié)點通過內(nèi)部定時任務(wù)發(fā)現(xiàn)自身復(fù)制的主節(jié)點進(jìn)入客觀下線時,將會觸發(fā)故障恢復(fù)流程。流程說明如下:
- 資格檢查:每個從節(jié)點都要檢查最后與主節(jié)點斷線時間,判斷是否有資格替換故障的主節(jié)點。如果從節(jié)點與主節(jié)點斷線時間超過cluster-node-time*cluster-slave-validity-factor,則當(dāng)前從節(jié)點不具備故障轉(zhuǎn)移資格。參數(shù)cluster-slave-validity-factor用于從節(jié)點的有效因子,默認(rèn)為10。
- 準(zhǔn)備選舉時間:當(dāng)從節(jié)點符合故障轉(zhuǎn)移資格后,更新觸發(fā)故障選舉的時間,只有到達(dá)該時間后才能執(zhí)行后續(xù)流程。之所以采用延遲觸發(fā)機(jī)制,主要是通過對多個從節(jié)點使用不同的延遲選舉時間來支持優(yōu)先級問題。復(fù)制偏移量越大說明從節(jié)點延遲越低,那么它應(yīng)該具有更高的優(yōu)先級來替換故障主節(jié)點。所有的從節(jié)點中復(fù)制偏移量最大的將提前觸發(fā)故障選舉流程。
- 發(fā)起選舉:當(dāng)從節(jié)點定時任務(wù)檢測到達(dá)故障選舉時間(failover_auth_time)到達(dá)后,發(fā)起選舉流程如下:
- 更新配置紀(jì)元:配置紀(jì)元是一個只增不減的整數(shù),每個主節(jié)點自身維護(hù)一個配置紀(jì)元(clusterNode.configEpoch)標(biāo)示當(dāng)前主節(jié)點的版本,所有主節(jié)點的配置紀(jì)元都不相等,從節(jié)點會復(fù)制主節(jié)點的配置紀(jì)元。整個集群又維護(hù)一個全局的配置紀(jì)元(clusterState.current Epoch),用于記錄集群內(nèi)所有主節(jié)點配置紀(jì)元的最大版本。
- 廣播選舉消息:在集群內(nèi)廣播選舉消息(FAILOVER_AUTH_REQUEST),并記錄已發(fā)送過消息的狀態(tài),保證該從節(jié)點在一個配置紀(jì)元內(nèi)只能發(fā)起一次選舉。消息內(nèi)容如同ping消息只是將type類型變?yōu)镕AILOVER_AUTH_REQUEST。
- 選舉投票:只有持有槽的主節(jié)點才會處理故障選舉消息(FAILOVER_AUTH_REQUEST),因為每個持有槽的節(jié)點在一個配置紀(jì)元內(nèi)都有唯一的一張選票,當(dāng)接到第一個請求投票的從節(jié)點消息時回復(fù)FAILOVER_AUTH_ACK消息作為投票,之后相同配置紀(jì)元內(nèi)其他從節(jié)點的選舉消息將忽略。
- 替換主節(jié)點:當(dāng)從節(jié)點收集到足夠的選票之后,觸發(fā)替換主節(jié)點操作:
- 當(dāng)前從節(jié)點取消復(fù)制變?yōu)橹鞴?jié)點。
- 執(zhí)行clusterDelSlot操作撤銷故障主節(jié)點負(fù)責(zé)的槽,并執(zhí)行clusterAddSlot把這些槽委派給自己。
- 向集群廣播自己的pong消息,通知集群內(nèi)所有的節(jié)點當(dāng)前從節(jié)點變?yōu)橹鞴?jié)點并接管了故障主節(jié)點的槽信息。
- 執(zhí)行cluster info命令可以查看配置紀(jì)元信息。
- 配置紀(jì)元會跟隨ping/pong消息在集群內(nèi)傳播,當(dāng)發(fā)送方與接收方都是主節(jié)點且配置紀(jì)元相等時代表出現(xiàn)了沖突,nodeId更大的一方會遞增全局配置紀(jì)元并賦值給當(dāng)前節(jié)點來區(qū)分沖突。
- 配置紀(jì)元的主要作用:
- 標(biāo)示集群內(nèi)每個主節(jié)點的不同版本和當(dāng)前集群最大的版本。
- 每次集群發(fā)生重要事件時,這里的重要事件指出現(xiàn)新的主節(jié)點(新加入的或者由從節(jié)點轉(zhuǎn)換而來),從節(jié)點競爭選舉。都會遞增集群全局的配置紀(jì)元并賦值給相關(guān)主節(jié)點,用于記錄這一關(guān)鍵事件。
- 主節(jié)點具有更大的配置紀(jì)元代表了更新的集群狀態(tài),因此當(dāng)節(jié)點間進(jìn)行ping/pong消息交換時,如出現(xiàn)slots等關(guān)鍵信息不一致時,以配置紀(jì)元更大的一方為準(zhǔn),防止過時的消息狀態(tài)污染集群。
- 配置紀(jì)元的應(yīng)用場景有:
- 新節(jié)點加入。
- 槽節(jié)點映射沖突檢測。
- 從節(jié)點投票選舉沖突檢測。
- 之前在通過cluster setslot命令修改槽節(jié)點映射時,需要確保執(zhí)行請求的主節(jié)點本地配置紀(jì)元(configEpoch)是最大值,否則修改后的槽信息在消息傳播中不會被擁有更高的配置紀(jì)元的節(jié)點采納。由于Gossip通信機(jī)制無法準(zhǔn)確知道當(dāng)前最大的配置紀(jì)元在哪個節(jié)點,因此在槽遷移任務(wù)最后的cluster setslot {slot} node {nodeId}命令需要在全部主節(jié)點中執(zhí)行一遍。
- 從節(jié)點每次發(fā)起投票時都會自增集群的全局配置紀(jì)元,并單獨保存在clusterState.failover_auth_epoch變量中用于標(biāo)識本次從節(jié)點發(fā)起選舉的版本。
- 投票過程其實是一個領(lǐng)導(dǎo)者選舉的過程,如集群內(nèi)有N個持有槽的主節(jié)點代表有N張選票。由于在每個配置紀(jì)元內(nèi)持有槽的主節(jié)點只能投票給一個從節(jié)點,因此只能有一個從節(jié)點獲得N/2+ 1的選票,保證能夠找出唯一的從節(jié)點。
- Redis集群沒有直接使用從節(jié)點進(jìn)行領(lǐng)導(dǎo)者選舉,主要因為從節(jié)點數(shù)必須大于等于3個才能保證湊夠N/2+1個節(jié)點,將導(dǎo)致從節(jié)點資源浪費。使用集群內(nèi)所有持有槽的主節(jié)點進(jìn)行領(lǐng)導(dǎo)者選舉,即使只有一個從節(jié)點也可以完成選舉過程。當(dāng)從節(jié)點收集到N/2+ 1個持有槽的主節(jié)點投票時,從節(jié)點可以執(zhí)行替換主節(jié)點操作。
- 故障主節(jié)點也算在投票數(shù)內(nèi),假設(shè)集群內(nèi)節(jié)點規(guī)模是3主3從,其中有2個主節(jié)點部署在一臺機(jī)器上,當(dāng)這臺機(jī)器宕機(jī)時,由于從節(jié)點無法收集到3/2+1個主節(jié)點選票將導(dǎo)致故障轉(zhuǎn)移失敗。這個問題也適用于故障發(fā)現(xiàn)環(huán)節(jié)。因此部署集群時所有主節(jié)點最少需要部署在3臺物理機(jī)上才能避免單點問題。
- 投票作廢:每個配置紀(jì)元代表了一次選舉周期,如果在開始投票之后的cluster-node-timeout*2時間內(nèi)從節(jié)點沒有獲取足夠數(shù)量的投票,則本次選舉作廢。從節(jié)點對配置紀(jì)元自增并發(fā)起下一輪投票,直到選舉成功為止。
21、故障轉(zhuǎn)移之故障轉(zhuǎn)移時間
- 估算出故障轉(zhuǎn)移時間:
- 主觀下線(pfail)識別時間=cluster-node-timeout。
- 主觀下線狀態(tài)消息傳播時間<=cluster-node-timeout/2。消息通信機(jī)制對超過cluster-node-timeout/2未通信節(jié)點會發(fā)起ping消息,消息體在選擇包含哪些節(jié)點時會優(yōu)先選取下線狀態(tài)節(jié)點,所以通常這段時間內(nèi)能夠收集到半數(shù)以上主節(jié)點的pfail報告從而完成故障發(fā)現(xiàn)。
- 從節(jié)點轉(zhuǎn)移時間<=1000毫秒。由于存在延遲發(fā)起選舉機(jī)制,偏移量最大的從節(jié)點會最多延遲1秒發(fā)起選舉。通常第一次選舉就會成功,所以從節(jié)點執(zhí)行轉(zhuǎn)移時間在1秒以內(nèi)。
根據(jù)以上分析可以預(yù)估出故障轉(zhuǎn)移時間,如下:
failover-time (毫秒) ≤ cluster-node-timeout + cluster-node-timeout/2 + 1000
因此,故障轉(zhuǎn)移時間跟cluster-node-timeout參數(shù)息息相關(guān),默認(rèn)15秒。配置時可以根據(jù)業(yè)務(wù)容忍度做出適當(dāng)調(diào)整,但不是越小越好。
22、集群運維——集群完整性
- 為了保證集群完整性,默認(rèn)情況下當(dāng)集群16384個槽任何一個沒有指派到節(jié)點時整個集群不可用。執(zhí)行任何鍵命令返回(error)CLUSTERDOWN Hash slot not served錯誤。
- 當(dāng)持有槽的主節(jié)點下線時,從故障發(fā)現(xiàn)到自動完成轉(zhuǎn)移期間整個集群是不可用狀態(tài),對于大多數(shù)業(yè)務(wù)無法容忍這種情況, 因此建議將參數(shù)cluster-require-full-coverage配置為no,當(dāng)主節(jié)點故障時只影響它負(fù)責(zé)槽的相關(guān)命令執(zhí)行,不會影響其他主節(jié)點的可用性。
23、集群運維——帶寬消耗
- 集群內(nèi)Gossip消息通信本身會消耗帶寬,官方建議集群最大規(guī)模在1000以內(nèi),也是出于對消息通信成本的考慮,因此單集群不適合部署超大規(guī)模的節(jié)點。
- 節(jié)點間消息通信對帶寬的消耗體現(xiàn)在以下幾個方面:
- 消息發(fā)送頻率:跟cluster-node-timeout密切相關(guān),當(dāng)節(jié)點發(fā)現(xiàn)與其他節(jié)點最后通信時間超過cluster-node-timeout/2時會直接發(fā)送ping消息。
- 消息數(shù)據(jù)量:每個消息主要的數(shù)據(jù)占用包含:slots槽數(shù)組(2KB空間)和整個集群1/10的狀態(tài)數(shù)據(jù)(10個節(jié)點狀態(tài)數(shù)據(jù)約1KB)。
- 節(jié)點部署的機(jī)器規(guī)模:機(jī)器帶寬的上線是固定的,因此相同規(guī)模的集群分布的機(jī)器越多每臺機(jī)器劃分的節(jié)點越均勻,則集群內(nèi)整體的可用帶寬越高。
- 集群帶寬消耗主要分為:讀寫命令消耗+Gossip消息消耗。因此搭建Redis集群時需要根據(jù)業(yè)務(wù)數(shù)據(jù)規(guī)模和消息通信成本做出合理規(guī)劃:
- 在滿足業(yè)務(wù)需要的情況下盡量避免大集群。同一個系統(tǒng)可以針對不同業(yè)務(wù)場景拆分使用多套集群。這樣每個集群既滿足伸縮性和故障轉(zhuǎn)移要求,還可以規(guī)避大規(guī)模集群的弊端。
- 適度提高cluster-node-timeout降低消息發(fā)送頻率,同時cluster-node-timeout還影響故障轉(zhuǎn)移的速度,因此需要根據(jù)自身業(yè)務(wù)場景兼顧二者的平衡。
- 如果條件允許集群盡量均勻部署在更多機(jī)器上。避免集中部署,如集群有60個節(jié)點,集中部署在3臺機(jī)器上每臺部署20個節(jié)點,這時機(jī)器帶寬消耗將非常嚴(yán)重。
24、集群運維——Pub/Sub廣播問題
- Redis在2.0版本提供了Pub/Sub(發(fā)布/訂閱)功能,用于針對頻道實現(xiàn)消息的發(fā)布和訂閱。但是在集群模式下內(nèi)部實現(xiàn)對所有的publish命令都會向所有的節(jié)點進(jìn)行廣播,造成每條publish數(shù)據(jù)都會在集群內(nèi)所有節(jié)點傳播一次,加重帶寬負(fù)擔(dān)。
- 當(dāng)頻繁應(yīng)用 Pub/Sub功能時應(yīng)該避免在大量節(jié)點的集群內(nèi)使用,否則會嚴(yán)重消耗集群內(nèi)網(wǎng)絡(luò)帶寬。針對這種情況建議使用sentinel結(jié)構(gòu)專門用于Pub/Sub功能,從而規(guī)避這一問題。
25、集群運維——集群傾斜
- 集群傾斜指不同節(jié)點之間數(shù)據(jù)量和請求量出現(xiàn)明顯差異。
1)數(shù)據(jù)傾斜
- 數(shù)據(jù)傾斜主要分為以下幾種:
- 節(jié)點和槽分配嚴(yán)重不均。
- 針對每個節(jié)點分配的槽不均的情況,可以使用redis-trib.rb info {host:ip}進(jìn)行定位,會列舉出每個節(jié)點負(fù)責(zé)的槽和鍵總量以及每個槽平均鍵數(shù)量。
- 當(dāng)節(jié)點對應(yīng)槽數(shù)量不均勻時,可以使用redis-trib.rb rebalance命令進(jìn)行平衡。
- 不同槽對應(yīng)鍵數(shù)量差異過大。
- 鍵通過CRC16哈希函數(shù)映射到槽上,正常情況下槽內(nèi)鍵數(shù)量會相對均勻。但當(dāng)大量使用hash_tag時,會產(chǎn)生不同 的鍵映射到同一個槽的情況。特別是選擇作為hash_tag的數(shù)據(jù)離散度較差時,將加速槽內(nèi)鍵數(shù)量傾斜情況。
- 通過命令:cluster countkeysinslot{slot}可以獲取槽對應(yīng)的鍵數(shù)量,識別出哪些槽映射了過多的鍵。再通過命令cluster getkeysinslot{slot}{count}循環(huán)迭代出槽下所有的鍵。從而發(fā)現(xiàn)過度使用hash_tag的鍵。
- 集合對象包含大量元素。
- 對于大集合對象的識別可以使用redis-cli-- bigkeys命令識別。找出大集合之后可以根據(jù)業(yè)務(wù)場景進(jìn)行拆分。
- 同時集群槽數(shù)據(jù)遷移是對鍵執(zhí)行migrate操作完成,過大的鍵集合如幾百兆,容易造成migrate命令超時導(dǎo)致數(shù)據(jù)遷移失敗。
- 內(nèi)存相關(guān)配置不一致。
- 內(nèi)存相關(guān)配置指hash-max-ziplist-value、set-max-intset-entries等壓縮數(shù)據(jù)結(jié)構(gòu)配置。當(dāng)集群大量使用hash、set等數(shù)據(jù)結(jié)構(gòu)時,如果內(nèi)存壓縮數(shù)據(jù)結(jié)構(gòu)配置不一致,極端情況下會相差數(shù)倍的內(nèi)存,從而造成節(jié)點內(nèi)存量傾斜。
2)請求傾斜
- 集群內(nèi)特定節(jié)點請求量/流量過大將導(dǎo)致節(jié)點之間負(fù)載不均,影響集群均衡和運維成本。常出現(xiàn)在熱點鍵場景,當(dāng)鍵命令消耗較低時如小對象的get、set、incr等,即使請求量差異較大一般也不會產(chǎn)生負(fù)載嚴(yán)重不均。
- 但是當(dāng)熱點鍵對應(yīng)高算法復(fù)雜度的命令或者是大對象操作如hgetall、smembers等,會導(dǎo)致對應(yīng)節(jié)點負(fù)載過高的情況。避免方式如下:
- 合理設(shè)計鍵,熱點大集合對象做拆分或使用hmget替代hgetall避免整體讀取。
- 不要使用熱鍵作為hash_tag,避免映射到同一槽。
- 對于一致性要求不高的場景,客戶端可使用本地緩存減少熱鍵調(diào)用。
26、集群運維——集群讀寫分離
1)只讀連接
- 集群模式下從節(jié)點不接受任何讀寫請求,發(fā)送過來的鍵命令會重定向到負(fù)責(zé)槽的主節(jié)點上(其中包括它的主節(jié)點)。
- 當(dāng)需要使用從節(jié)點分擔(dān)主節(jié)點讀壓力時,可以使用readonly命令打開客戶端連接只讀狀態(tài)。之前的復(fù)制配置slave-read-only在集群模式下無效。當(dāng)開啟只讀狀態(tài)時,從節(jié)點接收讀命令處理流程變?yōu)?#xff1a;如果對應(yīng)的槽屬于自己正在復(fù)制的主節(jié)點則直接執(zhí)行讀命令,否則返回重定向信息。
- readonly命令是連接級別生效,因此每次新建連接時都需要執(zhí)行readonly開啟只讀狀態(tài)。執(zhí)行readwrite命令可以關(guān)閉連接只讀狀態(tài)。
2)讀寫分離
- 針對從節(jié)點故障問題,客戶端需要維護(hù)可用節(jié)點列表,集群提供了cluster slaves {nodeId}命令,返回nodeId對應(yīng)主節(jié)點下所有從節(jié)點信息,數(shù)據(jù)格式同cluster nodes。解析從節(jié)點列表信息,排除fail狀態(tài)節(jié)點,這樣客戶端對從節(jié)點的故障判定可以委托給集群處理,簡化維護(hù)可用從節(jié)點列表難度。
- 集群模式下讀寫分離涉及對客戶端修改如下:
- 維護(hù)每個主節(jié)點可用從節(jié)點列表。
- 針對讀命令維護(hù)請求節(jié)點路由。
- 從節(jié)點新建連接開啟readonly狀態(tài)。
- 集群模式下讀寫分離成本比較高,可以直接擴(kuò)展主節(jié)點數(shù)量提高集群性能,一般不建議集群模式下做讀寫分離。
- 集群讀寫分離有時用于特殊業(yè)務(wù)場景如:
- 利用復(fù)制的最終一致性使用多個從節(jié)點做跨機(jī)房部署降低讀命令網(wǎng)絡(luò)延遲。
- 主節(jié)點故障轉(zhuǎn)移時間過長,業(yè)務(wù)端把讀請求路由給從節(jié)點保證讀操作可用。
- 以上場景也可以在不同機(jī)房獨立部署Redis集群解決,通過客戶端多寫來維護(hù),讀命令直接請求到最近機(jī)房的Redis集群,或者當(dāng)一個集群節(jié)點故障時客戶端轉(zhuǎn)向另一個集群。
27、集群運維——手動故障轉(zhuǎn)移
- Redis集群提供了手動故障轉(zhuǎn)移功能:指定從節(jié)點發(fā)起轉(zhuǎn)移流程,主從節(jié)點角色進(jìn)行切換,從節(jié)點變?yōu)樾碌闹鞴?jié)點對外提供服務(wù),舊的主節(jié)點變?yōu)樗膹墓?jié)點。
- 在從節(jié)點上執(zhí)行cluster failover命令發(fā)起轉(zhuǎn)移流程,默認(rèn)情況下轉(zhuǎn)移期間客戶端請求會有短暫的阻塞,但不會丟失數(shù)據(jù),流程如下:
- 從節(jié)點通知主節(jié)點停止處理所有客戶端請求。
- 主節(jié)點發(fā)送對應(yīng)從節(jié)點延遲復(fù)制的數(shù)據(jù)。
- 從節(jié)點接收處理復(fù)制延遲的數(shù)據(jù),直到主從復(fù)制偏移量一致為止,保證復(fù)制數(shù)據(jù)不丟失。
- 從節(jié)點立刻發(fā)起投票選舉(這里不需要延遲觸發(fā)選舉)。選舉成功后斷開復(fù)制變?yōu)樾碌闹鞴?jié)點,之后向集群廣播主節(jié)點pong消息。
- 舊主節(jié)點接受到消息后更新自身配置變?yōu)閺墓?jié)點,解除所有客戶端請求阻塞,這些請求會被重定向到新主節(jié)點上執(zhí)行。
- 舊主節(jié)點變?yōu)閺墓?jié)點后,向新的主節(jié)點發(fā)起全量復(fù)制流程。
- 主從節(jié)點轉(zhuǎn)移后,新的從節(jié)點由于之前沒有緩存主節(jié)點信息無法使用部分復(fù)制功能,所以會發(fā)起全量復(fù)制,當(dāng)節(jié)點包含大量數(shù)據(jù)時會嚴(yán)重消耗CPU和網(wǎng)絡(luò)資源,線上不要頻繁操作。Redis4.0的Psync2將有效改善這一問題。
- 手動故障轉(zhuǎn)移的應(yīng)用場景主要如下:
- 主節(jié)點遷移:運維Redis集群過程中經(jīng)常遇到調(diào)整節(jié)點部署的問題,如節(jié)點所在的老機(jī)器替換到新機(jī)器等。由于從節(jié)點默認(rèn)不響應(yīng)請求可以安全下線關(guān)閉,但直接下線主節(jié)點會導(dǎo)致故障自動轉(zhuǎn)移期間主節(jié)點無法對外提供服務(wù),影響線上業(yè)務(wù)的穩(wěn)定性。這時可以使用手動故障轉(zhuǎn)移,把要下線的主節(jié)點安全的替換為從節(jié)點后,再做下線操作操作。
- 強(qiáng)制故障轉(zhuǎn)移。當(dāng)自動故障轉(zhuǎn)移失敗時,只要故障的主節(jié)點有存活的從節(jié)點就可以通過手動轉(zhuǎn)移故障強(qiáng)制讓從節(jié)點替換故障的主節(jié)點,保證集群的可用性。
- 自動故障轉(zhuǎn)移失敗的場景有:
- 主節(jié)點和它的所有從節(jié)點同時故障。這個問題需要通過調(diào)整節(jié)點機(jī)器部署拓?fù)渥鲆?guī)避,保證主從節(jié)點不在同一機(jī)器/機(jī)架上。除非機(jī)房內(nèi)大面積故障,否則兩臺機(jī)器/機(jī)架同時故障概率很低。
- 所有從節(jié)點與主節(jié)點復(fù)制斷線時間超過cluster-slave-validity-factor*cluster-node-tineout+repl-ping-slave-period,導(dǎo)致從節(jié)點被判定為沒有故障轉(zhuǎn)移資格,手動故障轉(zhuǎn)移從節(jié)點不做中斷超時檢查。
- 由于網(wǎng)絡(luò)不穩(wěn)定等問題,故障發(fā)現(xiàn)或故障選舉時間無法在cluster-node-timeout*2內(nèi)完成,流程會不斷重試,最終從節(jié)點復(fù)制中斷時間超時,失去故障轉(zhuǎn)移資格無法完成轉(zhuǎn)移。
- 集群內(nèi)超過一半以上的主節(jié)點同時故障。
- 根據(jù)以上情況,cluster failover命令提供了兩個參數(shù)force/takeover提供支持:、
- cluster failover force——用于當(dāng)主節(jié)點宕機(jī)且無法自動完成故障轉(zhuǎn)移情況。從節(jié)點接到cluster failover force請求時,從節(jié)點直接發(fā)起選舉,不再跟主節(jié)點確認(rèn)復(fù)制偏移量(從節(jié)點復(fù)制延遲的數(shù)據(jù)會丟失),當(dāng)從節(jié)點選舉成功后替換為新的主節(jié)點并廣播集群配置。
- cluster failover takeover——用于集群內(nèi)超過一半以上主節(jié)點故障的場景,因為從節(jié)點無法收到半數(shù)以上主節(jié)點投票,所以無法完成選舉過程。可以執(zhí)行cluster failover takeover強(qiáng)制轉(zhuǎn)移,接到命令的從節(jié)點不再進(jìn)行選舉流程而是直接更新本地配置紀(jì)元并替換主節(jié)點。takeover故障轉(zhuǎn)移由于沒有通過領(lǐng)導(dǎo)者選舉發(fā)起故障轉(zhuǎn)移,會導(dǎo)致配置紀(jì)元存在沖突的可能。當(dāng)沖突發(fā)生時,集群會以nodeId字典序更大的一方配置為準(zhǔn)。因此要小心集群分區(qū)后,手動執(zhí)行takeover導(dǎo)致的集群沖突問題。
- 在集群可以自動完成故障轉(zhuǎn)移的情況下,不要使用cluster failover takeover強(qiáng)制干擾集群選舉機(jī)制,該操作主要用于半數(shù)以上主節(jié)點故障時采取的強(qiáng)制措施,請慎用。
- 手動故障轉(zhuǎn)移時,在滿足當(dāng)前需求的情況下建議優(yōu)先級:cluster failver>cluster failover force>cluster failover takeover。
28、集群運維——數(shù)據(jù)遷移
- 應(yīng)用Redis集群時,常需要把單機(jī)Redis數(shù)據(jù)遷移到集群環(huán)境。redis-trib.rb工具提供了導(dǎo)入功能,用于數(shù)據(jù)從單機(jī)向集群環(huán)境遷移的場景。
redis-trib .rb import host :port --from <arg> --copy --replace - redis-trib.rb import命令內(nèi)部采用批量scan和migrate 的方式遷移數(shù)據(jù)。這種遷移方式存在以下缺點:
- 遷移只能從單機(jī)節(jié)點向集群環(huán)境導(dǎo)入數(shù)據(jù)。
- 不支持在線遷移數(shù)據(jù),遷移數(shù)據(jù)時應(yīng)用方必須停寫,無法平滑遷移數(shù)據(jù)。
- 遷移過程中途如果出現(xiàn)超時等錯誤,不支持?jǐn)帱c續(xù)傳只能重新全量導(dǎo)入。
- 使用單線程進(jìn)行數(shù)據(jù)遷移,大數(shù)據(jù)量遷移速度過慢。
- 唯品會開發(fā)的redis-migrate-tool(https://github.com/vipshop/redis-migrate-tool),該工具可滿足大多數(shù)Redis遷移需求,特點如下:
- 支持單機(jī)、Twemproxy、Redis Cluster 、RDB/AOF等多種類型的數(shù)據(jù)遷移。
- 工具模擬成從節(jié)點基于復(fù)制流遷移數(shù)據(jù),從而支持在線遷移數(shù)據(jù),業(yè)務(wù)方不需要停寫。
- 采用多線程加速數(shù)據(jù)遷移過程且提供數(shù)據(jù)校驗和查看遷移狀態(tài)等功能。
總結(jié)
以上是生活随笔為你收集整理的【Redis】《Redis 开发与运维》笔记-Chapter10-集群的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 通过指针便利图像元素
- 下一篇: linux cmake编译源码,linu