在之前的《來(lái)聊聊NoSql》一文中,已經(jīng)說了redis三種集群模式中的主從和哨兵,接下來(lái)再看看redis-cluster怎么玩。本文基于redis-cluster官方文檔(https:///topics/cluster-tutorial),將其細(xì)化具體化,每一個(gè)操作過程都詳細(xì)記錄,如果官方文檔讀起來(lái)有點(diǎn)吃力,本文會(huì)是你不錯(cuò)的選擇。本文內(nèi)容包括:
一、redis-cluster介紹
1、有了哨兵為什么還要搞redis-cluster?
之前說的哨兵,一般是一主二從,寫數(shù)據(jù)都在master,讀數(shù)據(jù)在slave,當(dāng)master掛了之后會(huì)從兩個(gè)slave中自動(dòng)選出一個(gè)作為新的master??此坪芡昝?,但問題就在slave是read only的,只能讀數(shù)據(jù),寫數(shù)據(jù)的操作全都在master進(jìn)行,對(duì)于寫操作來(lái)說,redis相當(dāng)于還是一個(gè)單機(jī)版的。如果對(duì)寫操作并發(fā)很高,那可能就撐不住了,因此redis-cluster誕生。
2、redis-cluster機(jī)制:
redis-cluster引入了一個(gè)哈希槽(slot)的概念,讓數(shù)據(jù)與哈希槽關(guān)聯(lián),哈希槽再與節(jié)點(diǎn)關(guān)聯(lián)。這樣做的好處就是方便集群節(jié)點(diǎn)的增加或刪除。假如沒有哈希槽,數(shù)據(jù)直接和節(jié)點(diǎn)關(guān)聯(lián),也就是key直接和節(jié)點(diǎn)關(guān)聯(lián),如果要?jiǎng)h除一個(gè)節(jié)點(diǎn),那這個(gè)節(jié)點(diǎn)上的所有key都需要一個(gè)個(gè)地移走,比較麻煩;有了哈希槽,就可以將該節(jié)點(diǎn)上的哈希槽全部移走,比較方便。就好比有一些豆子,沒有哈希槽需要一個(gè)個(gè)地?fù)炱饋?lái),有哈希槽就是有個(gè)碗裝著這些豆子,直接把碗拿起來(lái)就好了。redis-cluster中固定有16384個(gè)哈希槽,假如redis-cluster中有6個(gè)redis實(shí)例,3個(gè)master,每個(gè)master帶1個(gè)(沒有固定是1個(gè),可以是N個(gè))slave,比如3個(gè)master是A、B、C,它們的slave分別為A1、B1、C1,整個(gè)集群對(duì)外表現(xiàn)為一個(gè)整體。當(dāng)你執(zhí)行set k1 v1
的時(shí)候,到底是set到哪臺(tái)master上去了呢?還是3臺(tái)master都有?其實(shí)是每個(gè)master都會(huì)分配到一部分哈希槽,比如:
set的時(shí)候,對(duì)key經(jīng)過一番計(jì)算:slot = CRC16(key) % 16384
,就可以拿到哈希槽slot
,就可以判斷該key要set到哪個(gè)master上。
3、slave的作用是什么?
當(dāng)有數(shù)據(jù)落在A上的時(shí)候,給客戶端返回成功,并且異步將數(shù)據(jù)復(fù)制到A1上。如果A掛了,那么A1就會(huì)成為master繼續(xù)提供服務(wù)。但如果A和A1都掛了,那么整個(gè)集群都將不能正常提供服務(wù),所以每個(gè)master可以多整幾個(gè)slave,保證高可用。而且正因?yàn)閺膍aster復(fù)制數(shù)據(jù)到slave是異步的,所以就有可能master給客戶端返回成功了,還沒來(lái)得及將數(shù)據(jù)復(fù)制到slave的時(shí)候,master掛了,然后slave變成了master,而這個(gè)master上是沒有剛才寫的數(shù)據(jù)的,這就出現(xiàn)了寫丟失的情況。如果需要保證強(qiáng)一致性,就要用同步寫的方式,但這將會(huì)犧牲性能。
二、redis-cluster的搭建
我這里用的是redis5.0
,5.0以下的版本創(chuàng)建集群略有不同。
1、安裝redis:
首先確保系統(tǒng)已經(jīng)安裝了gcc環(huán)境,然后在opt目錄下新建redis-cluster
目錄,下載redis5.0的包到這個(gè)目錄,再然后tar -zxvf redis5.0.xx.tar.gz
解壓redis,將解壓出來(lái)的目錄重命名為redis-server
,再進(jìn)入redis-server
目錄執(zhí)行make
,最后進(jìn)入src
目錄下執(zhí)行./redis-server
看看能否成功啟動(dòng)redis。
2、準(zhǔn)備工作:
- 在
/opt/redis-cluster
目錄下新建redis-conf
目錄; - 在上一步的
redis-conf
目錄下新建700x
目錄,我這里新建的是從7001到7006六個(gè)目錄;
3、修改redis.conf:
700x下的redis.conf文件要修改的地方如下:
# 原本是bind 127.0.0.1,改成如下或者直接注釋掉
bind 0.0.0.0
# 改端口,與目錄名對(duì)應(yīng)
port 700x
# 設(shè)置后臺(tái)啟動(dòng)
daemonize yes
# 設(shè)置進(jìn)程文件名
pidfile /var/run/redis_700x.pid
# 設(shè)置日志目錄和日志文件名
logfile /opt/redis-cluster/redis-conf/700x/node.log
# 開啟集群模式
cluster-enabled yes
# 集群配置文件的位置,這個(gè)啟動(dòng)集群后會(huì)自動(dòng)生成,這里是設(shè)置它生成的目錄和文件名
cluster-config-file /opt/redis-cluster/redis-conf/700x/nodes.conf
# 集群節(jié)點(diǎn)超時(shí)時(shí)間(毫秒)
cluster-node-timeout 15000
4、啟動(dòng)集群:
依次用上面修改的6份redis.conf啟動(dòng)6個(gè)redis實(shí)例,然后執(zhí)行如下命令:
/opt/redis-cluster/redis-server/src/redis-cli --cluster create 192.168.x.xx:7001 192.168.x.xx:7002 192.168.x.xx:7003 192.168.x.xx:7004 192.168.x.xx:7005 192.168.x.xx:7006 --cluster-replicas 1
--cluster-replicas 1
表示為每個(gè)主機(jī)創(chuàng)建一個(gè)從機(jī)。執(zhí)行該命令后,如果出現(xiàn)下面的提示信息,集群就啟動(dòng)成功了。
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
5、測(cè)試集群可用性:
進(jìn)入src目錄下執(zhí)行./redis-cli -c -p 700x
,任意連接一臺(tái),然后執(zhí)行cluster nodes
,就可以看到集群情況了。比如我的執(zhí)行后返回如下信息:
9f23a60b15fa1f0fab2e6393a84c4b78b3852fb6 192.168.2.43:7004@17004 slave fe163465d4c8b8a8a30d008d586a4ca56ed6d6db 0 1596642459801 4 connected
515f77f206d539cf3789cfa81bc678a3ea3697a5 192.168.2.43:7006@17006 slave 3ce363e5870562651ccf602f9b9a7a9b121c85a9 0 1596642458000 6 connected
fbcb70deb8917b3d84bb5d524657a5b674c43f0a 192.168.2.43:7005@17005 slave 2be934ea0e232d9a98796578a4a9e813fa4dda80 0 1596642457787 5 connected
2be934ea0e232d9a98796578a4a9e813fa4dda80 192.168.2.43:7002@17002 master - 0 1596642458794 2 connected 5461-10922
3ce363e5870562651ccf602f9b9a7a9b121c85a9 192.168.2.43:7003@17003 master - 0 1596642459000 3 connected 10923-16383
fe163465d4c8b8a8a30d008d586a4ca56ed6d6db 192.168.2.43:7001@17001 myself,master - 0 1596642457000 1 connected 0-5460
從返回的信息中可以知道,7001的slave是7004,7002的slave是7005,7003的slave是7006。然后執(zhí)行set k1 v1
,會(huì)返回如下信息:
127.0.0.1:7001> set k1 v1
-> Redirected to slot [12706] located at 192.168.2.43:7003
OK
192.168.2.43:7003>
它會(huì)對(duì)k1
進(jìn)行計(jì)算,最后根據(jù)計(jì)算結(jié)果發(fā)現(xiàn)它應(yīng)該落在7003主機(jī)所擁有的哈希槽中,就會(huì)自動(dòng)轉(zhuǎn)到7003上。多設(shè)置幾個(gè)值,就會(huì)發(fā)現(xiàn)它會(huì)自動(dòng)的分配到不同的主機(jī)上,get取值的時(shí)候也會(huì)自動(dòng)切換到對(duì)應(yīng)的主機(jī)上。
6、集群中的某一臺(tái)主機(jī)掛了咋整?
ps -ef | grep redis | grep -v grep
查看進(jìn)程號(hào),然后將7001端口的redis干掉,再去src目錄下執(zhí)行./redis-cli -c -p 700x
連接redis,最后cluster nodes
查看集群情況,返回如下信息:
fbcb70deb8917b3d84bb5d524657a5b674c43f0a 192.168.2.43:7005@17005 slave 2be934ea0e232d9a98796578a4a9e813fa4dda80 0 1596643307980 5 connected
515f77f206d539cf3789cfa81bc678a3ea3697a5 192.168.2.43:7006@17006 slave 3ce363e5870562651ccf602f9b9a7a9b121c85a9 0 1596643307000 6 connected
9f23a60b15fa1f0fab2e6393a84c4b78b3852fb6 192.168.2.43:7004@17004 master - 0 1596643306969 7 connected 0-5460
3ce363e5870562651ccf602f9b9a7a9b121c85a9 192.168.2.43:7003@17003 master - 0 1596643306000 3 connected 10923-16383
2be934ea0e232d9a98796578a4a9e813fa4dda80 192.168.2.43:7002@17002 myself,master - 0 1596643305000 2 connected 5461-10922
fe163465d4c8b8a8a30d008d586a4ca56ed6d6db 192.168.2.43:7001@17001 master,fail - 1596643272364 1596643269000 1 disconnected
發(fā)現(xiàn)7001的狀態(tài)是disconnected,并且7001的從機(jī)7004已經(jīng)變成master了,備胎轉(zhuǎn)正了。這個(gè)時(shí)候在隨便set一些值,發(fā)現(xiàn)集群還是正常工作的。
如果再把7004也干掉,即7001和7004這個(gè)組合都宕機(jī)了,這個(gè)時(shí)候集群就不能正常提供服務(wù)了。再去操作就會(huì)報(bào)錯(cuò)(error) CLUSTERDOWN The cluster is down
。
如果7001掛了,7004成為master后,我再?gòu)?fù)活7001,那么7001是master還是slave還是與這個(gè)集群無(wú)關(guān)呢?答案是7001成了7004的slave,7004這個(gè)備胎永久轉(zhuǎn)正了。
7、redis自帶的集群?jiǎn)?dòng)腳本:
其實(shí)在redis的utils/create-cluster/目錄下,有個(gè)create-cluster
腳本,我們可以依次執(zhí)行
./create-cluster start
./create-cluster create
來(lái)創(chuàng)建集群,前面那些創(chuàng)建目錄改配置文件那些操作都不用做了,直接執(zhí)行這兩條命令就可以啟動(dòng)一個(gè)集群,端口從30001開始,到30006。要停止集群執(zhí)行如下命令即可。
./create-cluster stop
8、編寫shell腳本啟動(dòng)redis-cluster:
上面啟動(dòng)集群的方法雖然簡(jiǎn)單,但生產(chǎn)上一般還是會(huì)自己配置,可以自定義端口、日志文件位置等,方便維護(hù)。按照自己配置啟動(dòng)集群時(shí),我們要依次啟動(dòng)6個(gè)redis實(shí)例,然后再啟動(dòng)集群,總共有7條命令要執(zhí)行,其實(shí)可以編寫一個(gè)shell腳本,一鍵啟動(dòng)。啟動(dòng)腳本內(nèi)容如下:
echo "starting redis-cluster"
oldNodesNum=`ps -ef | grep redis | grep -v grep | wc -l`
if [ "${oldNodesNum}" -ne 0 ]
then
`ps aux | grep redis | grep cluster | grep -v grep | awk '{print $2}' | xargs kill -9`
fi
/opt/redis-cluster/redis-server/src/redis-server /opt/redis-cluster/redis-conf/7001/redis.conf
/opt/redis-cluster/redis-server/src/redis-server /opt/redis-cluster/redis-conf/7002/redis.conf
/opt/redis-cluster/redis-server/src/redis-server /opt/redis-cluster/redis-conf/7003/redis.conf
/opt/redis-cluster/redis-server/src/redis-server /opt/redis-cluster/redis-conf/7004/redis.conf
/opt/redis-cluster/redis-server/src/redis-server /opt/redis-cluster/redis-conf/7005/redis.conf
/opt/redis-cluster/redis-server/src/redis-server /opt/redis-cluster/redis-conf/7006/redis.conf
/opt/redis-cluster/redis-server/src/redis-cli --cluster create 192.168.2.65:7001 192.168.2.65:7002 192.168.2.65:7003 192.168.2.65:7004 192.168.2.65:7005 192.168.2.65:7006 --cluster-replicas 1
nodesNum=`ps -ef | grep redis | grep -v grep | wc -l`
if [ "${nodesNum}" -lt 6 ]
then
`ps aux | grep redis | grep cluster | grep -v grep | awk '{print $2}' | xargs kill -9`
echo "started failed"
else
echo "started success"
fi
停止集群腳本如下:
ps aux | grep redis | grep cluster | grep -v grep | awk '{print $2}' | xargs kill -9
activeNodesNum=`ps -ef | grep redis | grep cluster | grep -v grep | wc -l`
if [ "${activeNodesNum}" -ne 0 ]
then
echo "shutdown failed"
else
echo "shutdown success"
fi
三、redis-cluster增刪node
假如現(xiàn)在master 7001(有key “k2”)和它的slave 7004掛掉了不能用了,想把它們倆從集群中移除,并且又啟動(dòng)了7007和7008兩個(gè)實(shí)例準(zhǔn)備加到集群中去,該怎么辦?
1、添加7007和7008到集群中:
./redis-cli --cluster add-node 192.168.2.65:7007 192.168.2.65:7001
執(zhí)行成功的話會(huì)返回如下信息:
>>> Send CLUSTER MEET to node 192.168.2.65:7007 to make it join the cluster.
[OK] New node added correctly.
./redis-cli --cluster info 192.168.2.65:7001
正常情況會(huì)返回如下信息:
192.168.2.65:7001 (1e7122a5...) -> 1 keys | 5461 slots | 1 slaves.
192.168.2.65:7007 (e22cc9ff...) -> 0 keys | 0 slots | 0 slaves.
192.168.2.65:7003 (4953107e...) -> 1 keys | 5461 slots | 1 slaves.
192.168.2.65:7002 (044b04f4...) -> 0 keys | 5462 slots | 1 slaves.
[OK] 2 keys in 4 masters.
可以看到集群中已經(jīng)有4個(gè)master了,7007是master但是沒有slave。
- 將7008添加作為7007的slave:首先執(zhí)行
./redis-cli --cluster check 192.168.2.65:7001
查看集群中各個(gè)實(shí)例的id,記住7007的id,然后執(zhí)行:
./redis-cli --cluster add-node --cluster-slave --cluster-master-id <7007的id> 192.168.2.65:7008 192.168.2.65:7001
正常情況會(huì)返回如下信息:
[OK] All 16384 slots covered.
>>> Send CLUSTER MEET to node 192.168.2.65:7008 to make it join the cluster.
Waiting for the cluster to join
>>> Configure node as replica of 192.168.2.65:7007.
[OK] New node added correctly
再次執(zhí)行如下命令查看當(dāng)前集群情況:
./redis-cli --cluster info 192.168.2.65:7001
返回了如下信息:
192.168.2.65:7001 (1e7122a5...) -> 1 keys | 5461 slots | 1 slaves.
192.168.2.65:7007 (e22cc9ff...) -> 0 keys | 0 slots | 1 slaves.
192.168.2.65:7003 (4953107e...) -> 1 keys | 5461 slots | 1 slaves.
192.168.2.65:7002 (044b04f4...) -> 0 keys | 5462 slots | 1 slaves.
[OK] 2 keys in 4 masters.
7007和7008已經(jīng)成功地加到集群中了,但是0 slots,即沒有分配到哈希槽。
2、轉(zhuǎn)移哈希槽:
現(xiàn)要將7001的哈希槽全部轉(zhuǎn)移到7007上。執(zhí)行如下命令:
./redis-cli --cluster reshard 192.168.2.65:7001 --cluster-from <7001的id> --cluster-to <7007的id> --cluster-slots 5461
5461表示把7001上的全部哈希槽都轉(zhuǎn)移到7007上。執(zhí)行過程中會(huì)要你確認(rèn)是否轉(zhuǎn)移,輸入yes回車,若干秒后就會(huì)全部轉(zhuǎn)移成功,然后再次執(zhí)行如下命令查看集群信息:
./redis-cli --cluster info 192.168.2.65:7001
會(huì)返回如下信息:
192.168.2.65:7001 (1e7122a5...) -> 0 keys | 0 slots | 0 slaves.
192.168.2.65:7007 (e22cc9ff...) -> 1 keys | 5461 slots | 2 slaves.
192.168.2.65:7003 (4953107e...) -> 1 keys | 5461 slots | 1 slaves.
192.168.2.65:7002 (044b04f4...) -> 0 keys | 5462 slots | 1 slaves.
[OK] 2 keys in 4 masters.
從這信息中可以看到7001已經(jīng)是0 key 0 slots了 0 slaves了,而7007是 1 key 5461 slots 2 slaves。為什么是2 slaves?因?yàn)?004也跟7007混了。
3、移除節(jié)點(diǎn):
數(shù)據(jù)轉(zhuǎn)移完畢,就可以將7001和7004從集群中移除掉了。執(zhí)行如下命令即可:
./redis-cli --cluster del-node 192.168.2.65:7002 <7001的id>
執(zhí)行成功會(huì)返回如下信息:
>>> Removing node 1e7122a5a7fbe9b00409b7c9aac60ef33a7c87db from cluster 192.168.2.65:7002
>>> Sending CLUSTER FORGET messages to the cluster...
>>> SHUTDOWN the node.
再執(zhí)行./redis-cli --cluster info 192.168.2.65:7001
查看集群信息,結(jié)果如下:
192.168.2.65:7002 (044b04f4...) -> 0 keys | 5462 slots | 1 slaves.
192.168.2.65:7007 (e22cc9ff...) -> 1 keys | 5461 slots | 2 slaves.
192.168.2.65:7003 (4953107e...) -> 1 keys | 5461 slots | 1 slaves.
[OK] 2 keys in 3 masters.
再執(zhí)行./redis-cli --cluster del-node 192.168.2.65:7002 <7004的id>
移除7004,然后再查看集群信息,結(jié)果如下:
192.168.2.65:7002 (044b04f4...) -> 0 keys | 5462 slots | 1 slaves.
192.168.2.65:7007 (e22cc9ff...) -> 1 keys | 5461 slots | 1 slaves.
192.168.2.65:7003 (4953107e...) -> 1 keys | 5461 slots | 1 slaves.
[OK] 2 keys in 3 masters.
現(xiàn)在就完美了,成功地將 7001&7004這一對(duì)替換成了 7007&7008,現(xiàn)在連接上客戶端,get k2
,看看數(shù)據(jù)是否還在,結(jié)果返回如下信息:
-> Redirected to slot [449] located at 192.168.2.65:7007
"v2"
說明數(shù)據(jù)也還在,成功地從7001轉(zhuǎn)移到了7007上。