Redis 高可用集群原理和實踐
微信公眾號:運維開發(fā)故事,作者:鄭哥
Redis 集群是 Redis 提供的分布式數(shù)據(jù)庫方案,集群通過分片(sharding)來進行數(shù)據(jù)共享,并提供復制和故障轉移能力。
集群環(huán)境搭建
Redis 集群最少需要 3 個 master 節(jié)點,這里我們搭建 3 個master 節(jié)點,3 個 slave 及節(jié)點(由于我機器配置受限,直接通過端口的方式模擬集群搭建,本處只是實驗方便,生產環(huán)境不可采取此方案)。
環(huán)境搭建步驟如下:
- 簡單說明,首先我們先要定義集群節(jié)點的端口
7000-7005
然后配置文件復制 redis.conf 到對應的配置文件名。
IP | 端口 | 配置文件 |
---|---|---|
127.0.0.1 | 7000 | 7000/redis-7000.conf |
127.0.0.1 | 7001 | 7001/redis-7001.conf |
127.0.0.1 | 7002 | 7002/redis-7002.conf |
127.0.0.1 | 7003 | 7003/redis-7003.conf |
127.0.0.1 | 7004 | 7004/redis-7004.conf |
127.0.0.1 | 7005 | 7005/redis-7005.conf |
- 編輯 redis.conf 文件,主要修改以下的幾個配置(如果需要設置密碼需要配置
requirepass
和masterauth
)
daemonize yes
# 這個端口和上面的配置清單一致即可
port 7000
# 啟動集群模式
cluster-enabled yes
# 集群節(jié)點信息文件,這里700x最好和port對應上
cluster-config-file nodes-7000.conf
cluster-node-timeout 5000
appendonly yes
- 服務啟動,注意我們需要啟動所有的節(jié)點,命令如下:
# 啟動所有的服務 7000-7005 cd 7000 redis-server ./redis-7000.conf
- 初始化集群,通過
redis-cli --cluster create
命令初始化集群,命令如下(如果是生產環(huán)境,需要節(jié)點間 IP 以及端口是否可互相訪問):
redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 \ 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 \ --cluster-replicas 1
- 集群狀態(tài)查詢
-
登錄節(jié)點
redis-cli -c -h 127.0.0.1 -p 7000
, 注意一定要加-c
表示集群模式。 -
查詢集群狀態(tài)
cluster info
? etc redis-cli -c -h 127.0.0.1 -p 7000 127.0.0.1:7000> 127.0.0.1:7000> 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:6 cluster_my_epoch:1 cluster_stats_messages_ping_sent:263 cluster_stats_messages_pong_sent:270 cluster_stats_messages_sent:533 cluster_stats_messages_ping_received:270 cluster_stats_messages_pong_received:263 cluster_stats_messages_received:533 127.0.0.1:7000>
其他的集群創(chuàng)建方案 utils/create-cluster
我們可以在參考資料中找到創(chuàng)建方式(參考文檔:https://redis.io/docs/manual/scaling/#redis-cluster-101)
集群原理
槽指派機制
Redis Cluster 將所有數(shù)據(jù)劃分為 16384 個 slots(槽位),每個節(jié)點負責其中一部分槽位。槽位的信息存儲于每個節(jié)點中。
當 Redis Cluster 的客戶端來連接集群時,它也會得到一份集群的槽位配置信息并將其緩存在客戶端本地。這樣當客戶端要查找某個 key 時,可以直接定位到目標節(jié)點。同時因為槽位的信息可能會存在客戶端與服務器不一致的情況,還需要糾正機制來實現(xiàn)槽位信息的校驗調整。
槽位定位算法
Cluster 默認會對 key 值使用 crc16 算法進行 hash 得到一個整數(shù)值,然后用這個整數(shù)值對 16384 進行與操作來得到具體槽位。
源碼位置 src/cluster.c
中的 keyHashSlot 方法
crc16(key,keylen) & 0x3FFF
為什是 16384 可以看看這篇文章 :Redis 為什么是 16384 個槽 ?
查詢某個 key 那個節(jié)點上,方法如下:
# 查詢 key 所在的槽位
127.0.0.1:7000> cluster keyslot wahaha
(integer) 12318
# 查詢所有的 槽分布信息
# 可以得出結論:wahaha 這個 key 在 7002 這個節(jié)點上
127.0.0.1:7000> cluster slots
1) 1) (integer) 10923
2) (integer) 16383
3) 1) "127.0.0.1"
2) (integer) 7002
3) "de631f8ac9649f5d9fb12013dc01407f953c3299"
4) 1) "127.0.0.1"
2) (integer) 7004
3) "6ac6065d07eb44b674a181da897401ec4cea9571"
2) 1) (integer) 5461
2) (integer) 10922
3) 1) "127.0.0.1"
2) (integer) 7001
3) "e36fc81472afb04b9c88af1504a8e02647de1b13"
4) 1) "127.0.0.1"
2) (integer) 7003
3) "a89dcff90147f6cc9425ff0c6e4bead7017dc1e1"
3) 1) (integer) 0
2) (integer) 5460
3) 1) "127.0.0.1"
2) (integer) 7000
3) "0acfc8b3dd2223333a03bbcf856dd2a839d2e072"
4) 1) "127.0.0.1"
2) (integer) 7005
3) "57ddd19f38d8b748386944d15d32671fb5cb1570"
127.0.0.1:7000>
其實沒有這么麻煩,集群模式支持 跳轉重定位 我們直接 get 就可以跳轉過去。
跳轉重定位
當客戶端向一個錯誤的節(jié)點發(fā)出了指令,該節(jié)點會發(fā)現(xiàn)指令的 key 所在的槽位并不歸自己管理,這時它會向客戶端發(fā)送一個特殊的跳轉指令攜帶目標操作的節(jié)點地址,告訴客戶端去連這個節(jié)點去獲取數(shù)據(jù)??蛻舳耸盏街噶詈蟪颂D到正確的節(jié)點上去操作,還會同步更新糾正本地的槽位映射表緩存,后續(xù)所有 key 將使用新的槽位映射表。
大白話說:就是如果當前 key 是自己的節(jié)點的槽位就自己處理,如果不是自己的槽位,就轉向目標槽位的節(jié)點。
演示一下:
set abc sdl
set sbc sdl
image.png
重定位的過程如下:
集群通訊機制
redis cluster 節(jié)點間采取 gossip 協(xié)議進行通信
維護集群的元數(shù)據(jù)(集群節(jié)點信息,主從角色,節(jié)點數(shù)量,各節(jié)點共享的數(shù)據(jù)等)有兩種方式:
-
集中式
-
gossip
集中式
優(yōu)點在于元數(shù)據(jù)的更新和讀取,時效性非常好,一旦元數(shù)據(jù)出現(xiàn)變更立即就會更新到集中式的存儲中,其他節(jié)點讀取的時候立即就可以立即感知到;不足在于所有的元數(shù)據(jù)的更新壓力全部集中在一個地方,可能導致元數(shù)據(jù)的存儲壓力。很多中間件都會借助 zookeeper 集中式存儲元數(shù)據(jù)。
gossip
gossip 協(xié)議包含多種消息,包括ping,pong,meet,fail等等。
meet:某個節(jié)點發(fā)送meet給新加入的節(jié)點,讓新節(jié)點加入集群中,然后新節(jié)點就會開始與其他節(jié)點進行通信;
ping:每個節(jié)點都會頻繁給其他節(jié)點發(fā)送ping,其中包含自己的狀態(tài)還有自己維護的集群元數(shù)據(jù),互相通過ping交換元數(shù)據(jù)(類似自己感知到的集群節(jié)點增加和移除,hash slot信息等);
pong: 對ping和meet消息的返回,包含自己的狀態(tài)和其他信息,也可以用于信息廣播和更新;
fail: 某個節(jié)點判斷另一個節(jié)點fail之后,就發(fā)送fail給其他節(jié)點,通知其他節(jié)點,指定的節(jié)點宕機了。
gossip協(xié)議的優(yōu)點在于元數(shù)據(jù)的更新比較分散,不是集中在一個地方,更新請求會陸陸續(xù)續(xù),打到所有節(jié)點上去更新,有一定的延時,降低了壓力;缺點在于元數(shù)據(jù)更新有延時可能導致集群的一些操作會有一些滯后。
gossip 通信的 10000 端口
每個節(jié)點都有一個專門用于節(jié)點間gossip通信的端口,就是自己提供服務的端口號+10000,比如7001,那么用于節(jié)點間通信的就是17001端口。每個節(jié)點每隔一段時間都會往另外幾個節(jié)點發(fā)送ping消息,同時其他幾點接收到ping消息之后返回pong消息。
網(wǎng)絡抖動
真實世界的機房網(wǎng)絡往往并不是風平浪靜的,它們經(jīng)常會發(fā)生各種各樣的小問題。比如網(wǎng)絡抖動就是非常常見的一種現(xiàn)象,突然之間部分連接變得不可訪問,然后很快又恢復正常。
為解決這種問題,Redis Cluster 提供了一種選項cluster-node-timeout,表示當某個節(jié)點持續(xù) timeout 的時間失聯(lián)時,才可以認定該節(jié)點出現(xiàn)故障,需要進行主從切換。如果沒有這個選項,網(wǎng)絡抖動會導致主從頻繁切換 (數(shù)據(jù)的重新復制)。
集群選舉原理
當slave發(fā)現(xiàn)自己的master變?yōu)镕AIL狀態(tài)時,便嘗試進行Failover,以期成為新的master。由于掛掉的master可能會有多個slave,從而存在多個slave競爭成為master節(jié)點的過程, 其過程如下:
-
slave發(fā)現(xiàn)自己的master變?yōu)镕AIL
-
將自己記錄的集群currentEpoch加1,并廣播FAILOVER_AUTH_REQUEST 信息
-
其他節(jié)點收到該信息,只有master響應,判斷請求者的合法性,并發(fā)送FAILOVER_AUTH_ACK,對每一個epoch只發(fā)送一次ack
-
嘗試failover的slave收集master返回的FAILOVER_AUTH_ACK
-
slave收到超過半數(shù)master的ack后變成新Master(這里解釋了集群為什么至少需要三個主節(jié)點,如果只有兩個,當其中一個掛了,只剩一個主節(jié)點是不能選舉成功的)
-
slave廣播Pong消息通知其他集群節(jié)點。
從節(jié)點并不是在主節(jié)點一進入 FAIL 狀態(tài)就馬上嘗試發(fā)起選舉,而是有一定延遲,一定的延遲確保我們等待FAIL狀態(tài)在集群中傳播,slave如果立即嘗試選舉,其它masters或許尚未意識到FAIL狀態(tài),可能會拒絕投票
延遲計算公式:
DELAY = 500ms + random(0 ~ 500ms) + SLAVE_RANK * 1000ms
SLAVE_RANK表示此slave已經(jīng)從master復制數(shù)據(jù)的總量的rank。Rank越小代表已復制的數(shù)據(jù)越新。這種方式下,持有最新數(shù)據(jù)的slave將會首先發(fā)起選舉(理論上)。
集群腦裂數(shù)據(jù)丟失問題
Redis 集群沒有過半機制會有腦裂問題,網(wǎng)絡分區(qū)導致腦裂后多個主節(jié)點對外提供寫服務,一旦網(wǎng)絡分區(qū)恢復,會將其中一個主節(jié)點變?yōu)閺墓?jié)點,這時會有大量數(shù)據(jù)丟失。
規(guī)避方法可以在 redis 配置里加上參數(shù)(這種方法不可能百分百避免數(shù)據(jù)丟失,參考集群leader選舉機制):
// 寫數(shù)據(jù)成功最少同步的slave數(shù)量,這個數(shù)量可以模仿大于半數(shù)機制配置, // 比如集群總共三個節(jié)點可以配置1,加上leader就是2,超過了半數(shù) min-replicas-to-write 1
注意:這個配置在一定程度上會影響集群的可用性,比如slave要是少于1個,這個集群就算leader正常也不能提供服務了,需要具體場景權衡選擇。
集群是否完整才能對外提供服務
當redis.conf的配置cluster-require-full-coverage為no時,表示當負責一個插槽的主庫下線
且沒有相應的從庫進行故障恢復時,集群仍然可用,如果為yes則集群不可用(默認為 yes)。
Redis 集群為什么至少需要三個master節(jié)點,并且推薦節(jié)點數(shù)為奇數(shù)?
因為新master的選舉需要大于半數(shù)的集群master節(jié)點同意才能選舉成功,如果只有兩個master節(jié)點,當其中一個掛了,是達不到選舉新master的條件的。
奇數(shù)個master節(jié)點可以在滿足選舉該條件的基礎上節(jié)省一個節(jié)點,比如三個master節(jié)點和四個master節(jié)點的集群相比,大家如果都掛了一個master節(jié)點都能選舉新master節(jié)點,如果都掛了兩個master節(jié)點都沒法選舉新master節(jié)點了,所以奇數(shù)的master節(jié)點更多的是從節(jié)省機器資源角度出發(fā)說的。
Redis 集群對批量操作命令的支持
如何讓多個 key 落到一個槽里面 ?
對于類似mset,mget這樣的多個key的原生批量操作命令,redis集群只支持所有key落在同一slot的情況,如果有多個key一定要用mset命令在redis集群上操作,則可以在key的前面加上{XX},這樣參數(shù)數(shù)據(jù)分片hash計算的只會是大括號里的值,這樣能確保不同的key能落到同一slot里去,示例如下:
mset {user1}:1:name zhangsan {user1}:1:age 18
假設name和age計算的hash slot值不一樣,但是這條命令在集群下執(zhí)行,redis只會用大括號里的 user1 做hash slot計算,所以算出來的slot值肯定相同,最后都能落在同一slot。
對比: 哨兵leader選舉流程
當一個master服務器被某sentinel視為下線狀態(tài)后,該sentinel會與其他sentinel協(xié)商選出sentinel的leader進行故障轉移工作。每個發(fā)現(xiàn)master服務器進入下線的sentinel都可以要求其他sentinel選自己為sentinel的leader,選舉是先到先得。同時每個sentinel每次選舉都會自增配置紀元(選舉周期),每個紀元中只會選擇一個sentinel的leader。如果所有超過一半的sentinel選舉某sentinel作為leader。之后該sentinel進行故障轉移操作,從存活的slave中選舉出新的master,這個選舉過程跟集群的master選舉很類似。
哨兵集群只有一個哨兵節(jié)點,redis的主從也能正常運行以及選舉master,如果master掛了,那唯一的那個哨兵節(jié)點就是哨兵leader了,可以正常選舉新master。
不過為了高可用一般都推薦至少部署三個哨兵節(jié)點。為什么推薦奇數(shù)個哨兵節(jié)點原理跟集群奇數(shù)個master節(jié)點類似。
集群故障轉移
Redis 集群中的節(jié)點分為主節(jié)點(master)和從節(jié)點(slave),其中主節(jié)點用于處理槽,而從節(jié)點則用于復制某個主節(jié)點,并且在復制主節(jié)點下線時,代替主節(jié)點繼續(xù)處理命令請求。
故障檢測
集群中的每個節(jié)點都會定期地向集群中的其他節(jié)點發(fā)送PING消息,以此來檢測對方是否在線,如果接收PING消息的節(jié)點沒有在規(guī)定的時間內,向發(fā)送PING消息的節(jié)點返回PONG消息,那么發(fā)送PING消息的節(jié)點就會將接收PING消息的節(jié)點標記為疑似下線(probable fail,PFAIL)。
如果在一個集群里面,半數(shù)以上負責處理槽的主節(jié)點都將某個主節(jié)點x報告為疑似下線,那么這個主節(jié)點x將被標記為已下線(FAIL),將主節(jié)點x標記為已下線的節(jié)點會向集群廣播一條關于主節(jié)點x的FAIL消息,所有收到這條FAIL消息的節(jié)點都會立即將主節(jié)點x標記為已下線。
舉個例子,主節(jié)點7002和主節(jié)點7003都認為主節(jié)點7000進入了下線狀態(tài),并且主節(jié)點7001也認為主節(jié)點7000進入了疑似下線狀態(tài),在集群四個負責處理槽的主節(jié)點里面,有三個都將主節(jié)點7000標記為下線,數(shù)量已經(jīng)超過了半數(shù),所以主節(jié)點7001會將主節(jié)點7000標記為已下線,并向集群廣播一條關于主節(jié)點7000的FAIL消息,如圖所示:
故障轉移
當一個從節(jié)點發(fā)現(xiàn)自己正在復制的主節(jié)點進入了已下線狀態(tài)時,從節(jié)點將開始對下線主節(jié)點進行故障轉移,以下是故障轉移的執(zhí)行步驟:
-
復制下線主節(jié)點的所有從節(jié)點里面,會有一個從節(jié)點被選中。
-
被選中的從節(jié)點會執(zhí)行
SLAVEOF no one
命令,成為新的主節(jié)點。 -
新的主節(jié)點會撤銷所有對已下線主節(jié)點的槽指派,并將這些槽全部指派給自己。
-
新的主節(jié)點向集群廣播一條PONG消息,這條PONG消息可以讓集群中的其他節(jié)點立即知道這個節(jié)點已經(jīng)由從節(jié)點變成了主節(jié)點,并且這個主節(jié)點已經(jīng)接管了原本由已下線節(jié)點負責處理的槽。
-
新的主節(jié)點開始接收和自己負責處理的槽有關的命令請求,故障轉移完成。
重新選擇新節(jié)點
新的主節(jié)點是通過選舉產生的。以下是集群選舉新的主節(jié)點的方法:
-
集群的配置紀元是一個自增計數(shù)器,它的初始值為0。
-
當集群里的某個節(jié)點開始一次故障轉移操作時,集群配置紀元的值會被增一。
-
對于每個配置紀元,集群里每個負責處理槽的主節(jié)點都有一次投票的機會,而第一個向主節(jié)點要求投票的從節(jié)點將獲得主節(jié)點的投票。
-
當從節(jié)點發(fā)現(xiàn)自己正在復制的主節(jié)點進入已下線狀態(tài)時,從節(jié)點會向集群廣播一條CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST消息,要求所有收到這條消息、并且具有投票權的主節(jié)點向這個從節(jié)點投票。
-
如果一個主節(jié)點具有投票權(它正在負責處理槽),并且這個主節(jié)點尚未投票給其他從節(jié)點,那么主節(jié)點將向要求投票的從節(jié)點返回一條CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK消息,表示這個主節(jié)點支持從節(jié)點成為新的主節(jié)點。
-
每個參與選舉的從節(jié)點都會接收CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK消息,并根據(jù)自己收到了多少條這種消息來統(tǒng)計自己獲得了多少主節(jié)點的支持。
-
如果集群里有N個具有投票權的主節(jié)點,那么當一個從節(jié)點收集到大于等于N/2+1張支持票時,這個從節(jié)點就會當選為新的主節(jié)點。
-
因為在每一個配置紀元里面,每個具有投票權的主節(jié)點只能投一次票,所以如果有N個主節(jié)點進行投票,那么具有大于等于N/2+1張支持票的從節(jié)點只會有一個,這確保了新的主節(jié)點只會有一個。
-
如果在一個配置紀元里面沒有從節(jié)點能收集到足夠多的支持票,那么集群進入一個新的配置紀元,并再次進行選舉,直到選出新的主節(jié)點為止。
Cluster 選舉新主節(jié)點的方法和 Sentinel 的方法非常相似,因為兩者都是基于 Raft 算法的領頭選舉(leader election)方法來實現(xiàn)的。
水平拓展
拓展主節(jié)點
集群狀態(tài)查詢
127.0.0.1:7000> cluster nodes
6ac6065d07eb44b674a181da897401ec4cea9571 127.0.0.1:7004@17004 slave de631f8ac9649f5d9fb12013dc01407f953c3299 0 1653897401000 3 connected
de631f8ac9649f5d9fb12013dc01407f953c3299 127.0.0.1:7002@17002 master - 0 1653897402475 3 connected 10923-16383
a89dcff90147f6cc9425ff0c6e4bead7017dc1e1 127.0.0.1:7003@17003 slave e36fc81472afb04b9c88af1504a8e02647de1b13 0 1653897401956 2 connected
e36fc81472afb04b9c88af1504a8e02647de1b13 127.0.0.1:7001@17001 master - 0 1653897401541 2 connected 5461-10922
0acfc8b3dd2223333a03bbcf856dd2a839d2e072 127.0.0.1:7000@17000 myself,master - 0 1653897400000 1 connected 0-5460
57ddd19f38d8b748386944d15d32671fb5cb1570 127.0.0.1:7005@17005 slave 0acfc8b3dd2223333a03bbcf856dd2a839d2e072 0 1653897401000 1 connected
127.0.0.1:7000>
拓展規(guī)劃
我們在原始集群基礎上再增加一主(7006)一從(7007)
IP | 端口 | 配置文件 | 類型 |
---|---|---|---|
127.0.0.1 | 7000 | 7000/redis-7000.conf | master |
127.0.0.1 | 7001 | 7001/redis-7001.conf | master |
127.0.0.1 | 7002 | 7002/redis-7002.conf | master |
127.0.0.1 | 7003 | 7003/redis-7003.conf | slave |
127.0.0.1 | 7004 | 7004/redis-7004.conf | slave |
127.0.0.1 | 7005 | 7005/redis-7005.conf | slave |
127.0.0.1 | 7006 | 7006/redis-7006.conf | master |
127.0.0.1 | 7007 | 7007/redis-7007.conf | slave |
啟動節(jié)點
redis-server redis-7006.conf
redis-server redis-7007.conf
集群中加入節(jié)點
加入集群
? etc redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7001 >>> Adding node 127.0.0.1:7006 to cluster 127.0.0.1:7001 >>> Performing Cluster Check (using node 127.0.0.1:7001) M: e36fc81472afb04b9c88af1504a8e02647de1b13 127.0.0.1:7001 slots:[5461-10922] (5462 slots) master 1 additional replica(s) S: a89dcff90147f6cc9425ff0c6e4bead7017dc1e1 127.0.0.1:7003 slots: (0 slots) slave replicates e36fc81472afb04b9c88af1504a8e02647de1b13 S: 6ac6065d07eb44b674a181da897401ec4cea9571 127.0.0.1:7004 slots: (0 slots) slave replicates de631f8ac9649f5d9fb12013dc01407f953c3299 S: 57ddd19f38d8b748386944d15d32671fb5cb1570 127.0.0.1:7005 slots: (0 slots) slave replicates 0acfc8b3dd2223333a03bbcf856dd2a839d2e072 M: de631f8ac9649f5d9fb12013dc01407f953c3299 127.0.0.1:7002 slots:[10923-16383] (5461 slots) master 1 additional replica(s) M: 0acfc8b3dd2223333a03bbcf856dd2a839d2e072 127.0.0.1:7000 slots:[0-5460] (5461 slots) master 1 additional replica(s) [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered. >>> Send CLUSTER MEET to node 127.0.0.1:7006 to make it join the cluster. [OK] New node added correctly. ? etc
如下,7006加入了集群,并且默認是一個master節(jié)點:
? etc redis-cli -p 7001 cluster nodes
2109c2832177e8514174c6ef8fefd681076e28df 127.0.0.1:7006@17006 master - 0 1653919429534 0 connected
a89dcff90147f6cc9425ff0c6e4bead7017dc1e1 127.0.0.1:7003@17003 slave e36fc81472afb04b9c88af1504a8e02647de1b13 0 1653919430000 2 connected
6ac6065d07eb44b674a181da897401ec4cea9571 127.0.0.1:7004@17004 slave de631f8ac9649f5d9fb12013dc01407f953c3299 0 1653919430549 3 connected
57ddd19f38d8b748386944d15d32671fb5cb1570 127.0.0.1:7005@17005 slave 0acfc8b3dd2223333a03bbcf856dd2a839d2e072 0 1653919430549 1 connected
de631f8ac9649f5d9fb12013dc01407f953c3299 127.0.0.1:7002@17002 master - 0 1653919429129 3 connected 10923-16383
0acfc8b3dd2223333a03bbcf856dd2a839d2e072 127.0.0.1:7000@17000 master - 0 1653919430549 1 connected 0-5460
e36fc81472afb04b9c88af1504a8e02647de1b13 127.0.0.1:7001@17001 myself,master - 0 1653919429000 2 connected 5461-10922
設置和遷移分片
為集群分配分片
redis-cli --cluster reshard 127.0.0.1:7001
在執(zhí)行過程中會詢問,計劃遷移槽數(shù),遷移數(shù)據(jù)目標,以及遷移數(shù)據(jù)來源。
重新分配后的結果查詢 redis-cli -p 7001 cluster nodes
配置從 7006 的從節(jié)點 7007, 同樣也是先執(zhí)行加入集群的命令
redis-cli --cluster add-node 127.0.0.1:7007 127.0.0.1:7001
設置從節(jié)點
我們需要執(zhí)行 replicate 命令來指定當前節(jié)點(從節(jié)點)的主節(jié)點id為哪個,首先需要連接新加的7007節(jié)點的客戶端,然后使用集群命令進行操作,把當前的7007(slave)節(jié)點指定到一個主節(jié)點下(這里使用之前創(chuàng)建的7006主節(jié)點)
具體的命令如下:
# 查詢集群節(jié)點
? etc redis-cli -p 7001 cluster nodes
2109c2832177e8514174c6ef8fefd681076e28df 127.0.0.1:7006@17006 master - 0 1653920562718 7 connected 0-1332 5461-6794 10923-12255
a89dcff90147f6cc9425ff0c6e4bead7017dc1e1 127.0.0.1:7003@17003 slave e36fc81472afb04b9c88af1504a8e02647de1b13 0 1653920561710 2 connected
6ac6065d07eb44b674a181da897401ec4cea9571 127.0.0.1:7004@17004 slave de631f8ac9649f5d9fb12013dc01407f953c3299 0 1653920562000 3 connected
57ddd19f38d8b748386944d15d32671fb5cb1570 127.0.0.1:7005@17005 slave 0acfc8b3dd2223333a03bbcf856dd2a839d2e072 0 1653920562516 1 connected
8d935918d877a63283e1f3a1b220cdc8cb73c414 127.0.0.1:7007@17007 master - 0 1653920563000 0 connected
de631f8ac9649f5d9fb12013dc01407f953c3299 127.0.0.1:7002@17002 master - 0 1653920563000 3 connected 12256-16383
0acfc8b3dd2223333a03bbcf856dd2a839d2e072 127.0.0.1:7000@17000 master - 0 1653920561506 1 connected 1333-5460
e36fc81472afb04b9c88af1504a8e02647de1b13 127.0.0.1:7001@17001 myself,master - 0 1653920562000 2 connected 6795-10922
# 登錄到新加節(jié)點
? etc redis-cli -p 7007
127.0.0.1:7007>
# 制定當前當前節(jié)點的主節(jié)點 7006
127.0.0.1:7007> cluster replicate 2109c2832177e8514174c6ef8fefd681076e28df
OK
# 重新查詢集群狀態(tài)
127.0.0.1:7007> cluster nodes
57ddd19f38d8b748386944d15d32671fb5cb1570 127.0.0.1:7005@17005 slave 0acfc8b3dd2223333a03bbcf856dd2a839d2e072 0 1653920688403 1 connected
8d935918d877a63283e1f3a1b220cdc8cb73c414 127.0.0.1:7007@17007 myself,slave 2109c2832177e8514174c6ef8fefd681076e28df 0 1653920685000 7 connected
2109c2832177e8514174c6ef8fefd681076e28df 127.0.0.1:7006@17006 master - 0 1653920688000 7 connected 0-1332 5461-6794 10923-12255
de631f8ac9649f5d9fb12013dc01407f953c3299 127.0.0.1:7002@17002 master - 0 1653920687000 3 connected 12256-16383
0acfc8b3dd2223333a03bbcf856dd2a839d2e072 127.0.0.1:7000@17000 master - 0 1653920687000 1 connected 1333-5460
6ac6065d07eb44b674a181da897401ec4cea9571 127.0.0.1:7004@17004 slave de631f8ac9649f5d9fb12013dc01407f953c3299 0 1653920688099 3 connected
a89dcff90147f6cc9425ff0c6e4bead7017dc1e1 127.0.0.1:7003@17003 slave e36fc81472afb04b9c88af1504a8e02647de1b13 0 1653920688504 2 connected
e36fc81472afb04b9c88af1504a8e02647de1b13 127.0.0.1:7001@17001 master - 0 1653920687392 2 connected 6795-10922
最后我們再次查詢,發(fā)現(xiàn) 7007 成功變成了 7008 的主節(jié)點
主節(jié)點下線
徹底刪除主節(jié)點,因為主節(jié)點中存在數(shù)據(jù),所以我們可以分為兩個步驟操作
-
數(shù)據(jù)遷移
-
節(jié)點下線
為了方便驗證,我先設置一個數(shù)據(jù)
127.0.0.1:7001> set sdl 123 -> Redirected to slot [11164] located at 127.0.0.1:7006 OK 127.0.0.1:7006> get sdl "123"
先下先從節(jié)點, 執(zhí)行一下命令:
redis-cli --cluster del-node 127.0.0.1:7007 8d935918d877a63283e1f3a1b220cdc8cb73c414
數(shù)據(jù)遷出
? etc redis-cli --cluster reshard 127.0.0.1:7001 >>> Performing Cluster Check (using node 127.0.0.1:7001) M: e36fc81472afb04b9c88af1504a8e02647de1b13 127.0.0.1:7001 slots:[10212-10922] (711 slots) master 1 additional replica(s) M: 2109c2832177e8514174c6ef8fefd681076e28df 127.0.0.1:7006 slots:[11471-12255] (785 slots) master S: a89dcff90147f6cc9425ff0c6e4bead7017dc1e1 127.0.0.1:7003 slots: (0 slots) slave replicates e36fc81472afb04b9c88af1504a8e02647de1b13 S: 6ac6065d07eb44b674a181da897401ec4cea9571 127.0.0.1:7004 slots: (0 slots) slave replicates de631f8ac9649f5d9fb12013dc01407f953c3299 S: 57ddd19f38d8b748386944d15d32671fb5cb1570 127.0.0.1:7005 slots: (0 slots) slave replicates 0acfc8b3dd2223333a03bbcf856dd2a839d2e072 M: de631f8ac9649f5d9fb12013dc01407f953c3299 127.0.0.1:7002 slots:[15624-16383] (760 slots) master 1 additional replica(s) M: 0acfc8b3dd2223333a03bbcf856dd2a839d2e072 127.0.0.1:7000 slots:[0-10211],[10923-11470],[12256-15623] (14128 slots) master 1 additional replica(s) [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered. How many slots do you want to move (from 1 to 16384)? 785 What is the receiving node ID? 0acfc8b3dd2223333a03bbcf856dd2a839d2e072 Please enter all the source node IDs. Type 'all' to use all the nodes as source nodes for the hash slots. Type 'done' once you entered all the source nodes IDs. Source node #1: 2109c2832177e8514174c6ef8fefd681076e28df Source node #2: done
查詢節(jié)點槽信息
節(jié)點下線
redis-cli --cluster del-node 127.0.0.1:7006 2109c2832177e8514174c6ef8fefd681076e28df
執(zhí)行后結果如下:
參考資料
-
《Redis 設計與實現(xiàn)》黃健宏
-
Redis 為什么是 16384 個槽 ?
-
https://blog.csdn.net/wanderstarrysky/article/details/118157751
-
https://segmentfault.com/a/1190000038373546
作者:鄭哥 運維開發(fā)故事
歡迎關注:運維開發(fā)故事