在之前的《來聊聊NoSql》一文中,已經說了redis三種集群模式中的主從和哨兵,接下來再看看redis-cluster怎么玩。本文基于redis-cluster官方文檔(https:///topics/cluster-tutorial),將其細化具體化,每一個操作過程都詳細記錄,如果官方文檔讀起來有點吃力,本文會是你不錯的選擇。本文內容包括:
一、redis-cluster介紹
1、有了哨兵為什么還要搞redis-cluster?
之前說的哨兵,一般是一主二從,寫數據都在master,讀數據在slave,當master掛了之后會從兩個slave中自動選出一個作為新的master。看似很完美,但問題就在slave是read only的,只能讀數據,寫數據的操作全都在master進行,對于寫操作來說,redis相當于還是一個單機版的。如果對寫操作并發(fā)很高,那可能就撐不住了,因此redis-cluster誕生。
2、redis-cluster機制:
redis-cluster引入了一個哈希槽(slot)的概念,讓數據與哈希槽關聯(lián),哈希槽再與節(jié)點關聯(lián)。這樣做的好處就是方便集群節(jié)點的增加或刪除。假如沒有哈希槽,數據直接和節(jié)點關聯(lián),也就是key直接和節(jié)點關聯(lián),如果要刪除一個節(jié)點,那這個節(jié)點上的所有key都需要一個個地移走,比較麻煩;有了哈希槽,就可以將該節(jié)點上的哈希槽全部移走,比較方便。就好比有一些豆子,沒有哈希槽需要一個個地撿起來,有哈希槽就是有個碗裝著這些豆子,直接把碗拿起來就好了。redis-cluster中固定有16384個哈希槽,假如redis-cluster中有6個redis實例,3個master,每個master帶1個(沒有固定是1個,可以是N個)slave,比如3個master是A、B、C,它們的slave分別為A1、B1、C1,整個集群對外表現(xiàn)為一個整體。當你執(zhí)行set k1 v1的時候,到底是set到哪臺master上去了呢?還是3臺master都有?其實是每個master都會分配到一部分哈希槽,比如:
set的時候,對key經過一番計算:slot = CRC16(key) % 16384,就可以拿到哈希槽slot,就可以判斷該key要set到哪個master上。
3、slave的作用是什么?
當有數據落在A上的時候,給客戶端返回成功,并且異步將數據復制到A1上。如果A掛了,那么A1就會成為master繼續(xù)提供服務。但如果A和A1都掛了,那么整個集群都將不能正常提供服務,所以每個master可以多整幾個slave,保證高可用。而且正因為從master復制數據到slave是異步的,所以就有可能master給客戶端返回成功了,還沒來得及將數據復制到slave的時候,master掛了,然后slave變成了master,而這個master上是沒有剛才寫的數據的,這就出現(xiàn)了寫丟失的情況。如果需要保證強一致性,就要用同步寫的方式,但這將會犧牲性能。
二、redis-cluster的搭建
我這里用的是redis5.0,5.0以下的版本創(chuàng)建集群略有不同。
1、安裝redis:
首先確保系統(tǒng)已經安裝了gcc環(huán)境,然后在opt目錄下新建redis-cluster目錄,下載redis5.0的包到這個目錄,再然后tar -zxvf redis5.0.xx.tar.gz解壓redis,將解壓出來的目錄重命名為redis-server,再進入redis-server目錄執(zhí)行make,最后進入src目錄下執(zhí)行./redis-server看看能否成功啟動redis。
2、準備工作:
- 在
/opt/redis-cluster目錄下新建redis-conf目錄; - 在上一步的
redis-conf目錄下新建700x目錄,我這里新建的是從7001到7006六個目錄;
3、修改redis.conf:
700x下的redis.conf文件要修改的地方如下:
# 原本是bind 127.0.0.1,改成如下或者直接注釋掉
bind 0.0.0.0
# 改端口,與目錄名對應
port 700x
# 設置后臺啟動
daemonize yes
# 設置進程文件名
pidfile /var/run/redis_700x.pid
# 設置日志目錄和日志文件名
logfile /opt/redis-cluster/redis-conf/700x/node.log
# 開啟集群模式
cluster-enabled yes
# 集群配置文件的位置,這個啟動集群后會自動生成,這里是設置它生成的目錄和文件名
cluster-config-file /opt/redis-cluster/redis-conf/700x/nodes.conf
# 集群節(jié)點超時時間(毫秒)
cluster-node-timeout 15000
4、啟動集群:
依次用上面修改的6份redis.conf啟動6個redis實例,然后執(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表示為每個主機創(chuàng)建一個從機。執(zhí)行該命令后,如果出現(xiàn)下面的提示信息,集群就啟動成功了。
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
5、測試集群可用性:
進入src目錄下執(zhí)行./redis-cli -c -p 700x,任意連接一臺,然后執(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,會返回如下信息:
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>
它會對k1進行計算,最后根據計算結果發(fā)現(xiàn)它應該落在7003主機所擁有的哈希槽中,就會自動轉到7003上。多設置幾個值,就會發(fā)現(xiàn)它會自動的分配到不同的主機上,get取值的時候也會自動切換到對應的主機上。
6、集群中的某一臺主機掛了咋整?
ps -ef | grep redis | grep -v grep查看進程號,然后將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的從機7004已經變成master了,備胎轉正了。這個時候在隨便set一些值,發(fā)現(xiàn)集群還是正常工作的。
如果再把7004也干掉,即7001和7004這個組合都宕機了,這個時候集群就不能正常提供服務了。再去操作就會報錯(error) CLUSTERDOWN The cluster is down。
如果7001掛了,7004成為master后,我再復活7001,那么7001是master還是slave還是與這個集群無關呢?答案是7001成了7004的slave,7004這個備胎永久轉正了。
7、redis自帶的集群啟動腳本:
其實在redis的utils/create-cluster/目錄下,有個create-cluster腳本,我們可以依次執(zhí)行
./create-cluster start
./create-cluster create
來創(chuàng)建集群,前面那些創(chuàng)建目錄改配置文件那些操作都不用做了,直接執(zhí)行這兩條命令就可以啟動一個集群,端口從30001開始,到30006。要停止集群執(zhí)行如下命令即可。
./create-cluster stop
8、編寫shell腳本啟動redis-cluster:
上面啟動集群的方法雖然簡單,但生產上一般還是會自己配置,可以自定義端口、日志文件位置等,方便維護。按照自己配置啟動集群時,我們要依次啟動6個redis實例,然后再啟動集群,總共有7條命令要執(zhí)行,其實可以編寫一個shell腳本,一鍵啟動。啟動腳本內容如下:
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掛掉了不能用了,想把它們倆從集群中移除,并且又啟動了7007和7008兩個實例準備加到集群中去,該怎么辦?
1、添加7007和7008到集群中:
./redis-cli --cluster add-node 192.168.2.65:7007 192.168.2.65:7001
執(zhí)行成功的話會返回如下信息:
>>> 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
正常情況會返回如下信息:
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.
可以看到集群中已經有4個master了,7007是master但是沒有slave。
- 將7008添加作為7007的slave:首先執(zhí)行
./redis-cli --cluster check 192.168.2.65:7001查看集群中各個實例的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
正常情況會返回如下信息:
[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í)行如下命令查看當前集群情況:
./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已經成功地加到集群中了,但是0 slots,即沒有分配到哈希槽。
2、轉移哈希槽:
現(xiàn)要將7001的哈希槽全部轉移到7007上。執(zhí)行如下命令:
./redis-cli --cluster reshard 192.168.2.65:7001 --cluster-from <7001的id> --cluster-to <7007的id> --cluster-slots 5461
5461表示把7001上的全部哈希槽都轉移到7007上。執(zhí)行過程中會要你確認是否轉移,輸入yes回車,若干秒后就會全部轉移成功,然后再次執(zhí)行如下命令查看集群信息:
./redis-cli --cluster info 192.168.2.65:7001
會返回如下信息:
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已經是0 key 0 slots了 0 slaves了,而7007是 1 key 5461 slots 2 slaves。為什么是2 slaves?因為7004也跟7007混了。
3、移除節(jié)點:
數據轉移完畢,就可以將7001和7004從集群中移除掉了。執(zhí)行如下命令即可:
./redis-cli --cluster del-node 192.168.2.65:7002 <7001的id>
執(zhí)行成功會返回如下信息:
>>> 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查看集群信息,結果如下:
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,然后再查看集群信息,結果如下:
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這一對替換成了 7007&7008,現(xiàn)在連接上客戶端,get k2,看看數據是否還在,結果返回如下信息:
-> Redirected to slot [449] located at 192.168.2.65:7007
"v2"
說明數據也還在,成功地從7001轉移到了7007上。