準(zhǔn)備工作
redis集群一般由多個(gè)節(jié)點(diǎn)組成,節(jié)點(diǎn)數(shù)至少為6個(gè)才能保證組成完整高可用的集群。建議為集群所有的節(jié)點(diǎn)配置統(tǒng)一的目錄,我一般習(xí)慣按照端口號(hào)區(qū)分節(jié)點(diǎn),如:/opt/redis/6379/conf/redis-6379.conf,/opt/redis/6380/conf/redis-6380.conf,所有的數(shù)據(jù)包括日志都存放在/opt/redis/{port}下。節(jié)點(diǎn)配置文件格式為redis-{port}.port,集群配置文件為node-{port}.conf。
bind 0.0.0.0
pidfile /var/run/redis_6379.pid
port 6379
logfile "6379.log"
dbfilename dump-6379.rdb
dir "/opt/redis/6379/"
appendfilename "appendonly-6379.aof"
daemonize yes
maxmemory 30m
cluster-enabled yes
cluster-config-file nodes-6379.conf
啟動(dòng)所有節(jié)點(diǎn)
redis-servier /opt/redis/6379/conf/redis-6379.conf
redis-servier?/opt/redis/6380/conf/redis-6380.conf
redis-servier?/opt/redis/6381/conf/redis-6381.conf
redis-servier?/opt/redis/6382/conf/redis-6382.conf
redis-servier?/opt/redis/6383/conf/redis-6383.conf
redis-servier?/opt/redis/6384/conf/redis-6384.conf
第一次啟動(dòng)時(shí),如果沒(méi)有集群配置文件,它會(huì)自動(dòng)創(chuàng)建一份,文件名稱(chēng)由cluster-config-file參數(shù)項(xiàng)控制。啟動(dòng)過(guò)程如下圖所示:

集群模式的redis除了原有的配置文件之外又加了一份集群配置文件。當(dāng)集群內(nèi)節(jié)點(diǎn)信息發(fā)生變化,如添加節(jié)點(diǎn),節(jié)點(diǎn)下線(xiàn),故障轉(zhuǎn)移等。節(jié)點(diǎn)會(huì)自動(dòng)保存集群狀態(tài)到配置文件中。需要注意的是,redis自動(dòng)維護(hù)集群配置文件,不要手動(dòng)修改,防止節(jié)點(diǎn)重啟時(shí)產(chǎn)生集群信息錯(cuò)亂。
如節(jié)點(diǎn)6379首次啟動(dòng)后生成集群配置如下:
cat /opt/redis/6379/nodes-6379.conf
04583d74493aed49e97973c84109a0bfe5ba63da :0@0 myself,master - 0 0 0 connected
vars currentEpoch 0 lastVoteEpoch 0
文件內(nèi)容紀(jì)錄集群初始狀態(tài),這里最重要的是節(jié)點(diǎn)ID,他是一個(gè)40位16進(jìn)制字符串,用于唯一標(biāo)識(shí)集群內(nèi)的一個(gè)節(jié)點(diǎn),之后很多集群操作都要借助于節(jié)點(diǎn)ID來(lái)完成。節(jié)點(diǎn)ID不同于運(yùn)行ID。節(jié)點(diǎn)ID在集群初始化時(shí)只創(chuàng)建一次,節(jié)點(diǎn)重啟時(shí)會(huì)加載集群配置文件進(jìn)行重用。每個(gè)節(jié)點(diǎn)目前只能識(shí)別出自己的節(jié)點(diǎn)信息。我們啟動(dòng)6個(gè)節(jié)點(diǎn),但每個(gè)節(jié)點(diǎn)彼此并不知道對(duì)方的存在,下面通過(guò)節(jié)點(diǎn)握手讓6個(gè)節(jié)點(diǎn)彼此建立聯(lián)系從而組成一個(gè)集群。
節(jié)點(diǎn)握手
節(jié)點(diǎn)握手是指一批運(yùn)行在集群模式下的節(jié)點(diǎn)通過(guò)Gossip協(xié)議彼此通信,達(dá)到感知對(duì)方的過(guò)程。
客戶(hù)端(假設(shè)現(xiàn)在是在6379上的客戶(hù)端)發(fā)起命令:
cluster meet {ip} {port}
假設(shè)命令為:cluster meet 127.0.0.1 6380
即是讓節(jié)點(diǎn)6379和6380節(jié)點(diǎn)進(jìn)行握手通信。cluster meet命令是一個(gè)異步命令,執(zhí)行之后立即返回。內(nèi)部發(fā)起與目標(biāo)節(jié)點(diǎn)進(jìn)行握手通信,如下圖:

1>節(jié)點(diǎn)6379本地創(chuàng)建6380節(jié)點(diǎn)信息對(duì)象,并發(fā)送meet消息。
2>節(jié)點(diǎn)6380接受到meet消息后,保存6379節(jié)點(diǎn)信息并回復(fù)pong消息。
3>之后節(jié)點(diǎn)彼此定期通過(guò)ping/pong消息進(jìn)行正常的節(jié)點(diǎn)通信。
下面分別執(zhí)行meet命令讓其他的節(jié)點(diǎn)加入到集群當(dāng)中,我們只需要在集群內(nèi)任意節(jié)點(diǎn)上執(zhí)行cluster meet命令加入新節(jié)點(diǎn),握手狀態(tài)會(huì)通過(guò)消息在集群內(nèi)傳播,這樣其他節(jié)點(diǎn)會(huì)自動(dòng)發(fā)現(xiàn)新節(jié)點(diǎn)并發(fā)起握手流程。
建立握手后集群還不能正常工作,這時(shí)集群處于下線(xiàn)狀態(tài),所有的數(shù)據(jù)讀寫(xiě)都被禁止。
通過(guò)cluster info命令可以獲取集群當(dāng)前狀態(tài):
127.0.0.1:6379> cluster info
cluster_state:fail
cluster_slots_assigned:0
cluster_slots_ok:0
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:0
cluster_current_epoch:5
cluster_my_epoch:1
cluster_stats_messages_ping_sent:897
cluster_stats_messages_pong_sent:736
cluster_stats_messages_meet_sent:6
cluster_stats_messages_sent:1639
cluster_stats_messages_ping_received:736
cluster_stats_messages_pong_received:753
cluster_stats_messages_received:1489
從輸出內(nèi)容可以看到,被分配的槽(cluster_slots_assigned)是0,由于目前所有的槽沒(méi)有分配到節(jié)點(diǎn),因此集群無(wú)法完成槽道節(jié)點(diǎn)的映射。只有當(dāng)16384個(gè)槽全部分配給節(jié)點(diǎn)后,集群才會(huì)進(jìn)入在線(xiàn)狀態(tài)。
分配槽
redis集群把所有的數(shù)據(jù)映射到16384個(gè)槽中。每個(gè)key會(huì)映射為一個(gè)固定的槽,只有當(dāng)節(jié)點(diǎn)分配了槽,才能響應(yīng)和這些槽關(guān)聯(lián)的鍵命令。通過(guò)cluster addslots命令為節(jié)點(diǎn)分配槽。
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}
注:6379分配了5462個(gè)槽,6380分配了5461個(gè)槽,6381分配了5461個(gè)槽,注意區(qū)間中的點(diǎn)只有兩個(gè)。
基本平均分配后,查看集群信息:
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:1
cluster_stats_messages_ping_sent:2253
cluster_stats_messages_pong_sent:2017
cluster_stats_messages_meet_sent:6
cluster_stats_messages_sent:4276
cluster_stats_messages_ping_received:2017
cluster_stats_messages_pong_received:2109
cluster_stats_messages_received:4126
當(dāng)前集群狀態(tài)是ok,集群進(jìn)入在線(xiàn)狀態(tài)。所有的槽都已經(jīng)分配給節(jié)點(diǎn),執(zhí)行cluster nodes命令可以看到節(jié)點(diǎn)和槽的分配關(guān)系:

可以看到目前還有3個(gè)節(jié)點(diǎn)沒(méi)有被使用,作為一個(gè)完整的集群,每個(gè)負(fù)責(zé)處理槽的節(jié)點(diǎn)應(yīng)該具有從節(jié)點(diǎn),保證當(dāng)它出現(xiàn)故障時(shí)可以自動(dòng)進(jìn)行故障轉(zhuǎn)移。集群模式下,redis節(jié)點(diǎn)角色分為主節(jié)點(diǎn)和從節(jié)點(diǎn)。首次啟動(dòng)的節(jié)點(diǎn)和被分配槽的節(jié)點(diǎn)都是主節(jié)點(diǎn),從節(jié)點(diǎn)負(fù)責(zé)復(fù)制主節(jié)點(diǎn)槽信息和相關(guān)數(shù)據(jù)。
使用命令可以讓一個(gè)節(jié)點(diǎn)成為從節(jié)點(diǎn)。
127.0.0.1:6382> cluster replicate 04583d74493aed49e97973c84109a0bfe5ba63da
127.0.0.1:6383> cluster replicate ae87f095caf8168fd300ee92dec28dfa09706141
127.0.0.1:6384> cluster replicate 3668135fd6acb06319299cd28e8705cb40eb38d5
redis集群模式下的主從復(fù)制支持全量復(fù)制和部分復(fù)制。
復(fù)制完成后集群關(guān)系如下:
6379(主)? <--- 6382(從)
6380(主)? <--- 6383(從)
6381(主)? <--- 6384(從)
到目前為止,我們依照redis協(xié)議手動(dòng)建立了一個(gè)集群。它由6個(gè)節(jié)點(diǎn)構(gòu)成,3個(gè)主節(jié)點(diǎn)負(fù)責(zé)處理槽和相關(guān)的數(shù)據(jù),3個(gè)從節(jié)點(diǎn)負(fù)責(zé)故障轉(zhuǎn)移。步驟繁雜,因此redis官方提供redis-trib.rb工具方便我們快速搭建集群。