1. 程式人生 > >Redis 超詳細的手動搭建Cluster叢集步驟

Redis 超詳細的手動搭建Cluster叢集步驟

# 功能概述 ​ Redis Cluster是Redis的自帶的官方分散式解決方案,提供資料分片、高可用功能,在3.0版本正式推出。 ​ 使用Redis Cluster能解決負載均衡的問題,內部採用雜湊分片規則: ​ 基礎架構圖如下所示: ![image-20210401200112675](https://images-1302522496.cos.ap-nanjing.myqcloud.com/img/image-20210401200112675.png) ​ 圖中最大的虛線部分為一個Cluster叢集,由6個Redis例項組成。 # 叢集分片 ​ 整個Cluster叢集中有16384個槽位,必須要將這些槽位分別規劃在3臺Master中。 ​ 如果有任意1個槽位沒有被分配,則叢集建立不成功。 ​ 當叢集中任意一個Master嘗試進行寫入操作後,會通過Hash演算法計算出該條資料應該落在哪一個Master節點上。 ​ 如下圖所示: ![image-20210401203018467](https://images-1302522496.cos.ap-nanjing.myqcloud.com/img/image-20210401203018467.png) ​ 情況1:如果你未指定任何引數就進行寫入,如在Master1上寫入資料,經過內部計算髮現該資料應該在Master2上寫入時,會提示你應該進入Master2寫入該條資料,執行並不會成功 ​ 情況2:如果你指定了一個特定引數進行寫入,如在Master1上寫入資料,經過內部計算髮現該資料應該在Master2上寫入時,會自動將寫入環境重定向至Master2,執行成功 ​ 同理,讀取資料也是這樣,這個過程叫做MOVED重定向,如果你是情況1進行操作則必須手動進行重定向,情況2則會自動進行重定向。 # 叢集通訊 ​ 叢集中各個節點的資訊是互通的,這種現象由Gossip(流言)協議產生。 ​ Gossip協議規定每個叢集節點之間互相交換資訊,使其能夠彼此知道對方的狀態。 ​ 在通訊建立時,叢集中的每一個節點都會單獨的開闢一個TCP通道,用於與其他節點進行通訊,這個通訊埠會在基礎埠上+10000。 ​ 通訊建立成功後,每個節點在固定週期內通過特定規則選擇節點來發送ping訊息(心跳機制)。 ​ 當收到ping訊息的節點則會使用pong訊息作為迴應,也就是說,當有一個新節點加入後,一瞬間叢集中所有的其他節點也能夠獲取到該資訊。 ​ Gossip協議的主要職責就是進行叢集中節點的資訊交換,常見的Gossip協議訊息有以下幾點區分: - meet:用於通知新節點加入,訊息傳送者通知接受者加入到當前叢集 - ping:叢集內每個節點與其他節點進行心跳檢測的命令,用於檢測其他節點是否線上,除此之外還能交換其他額外資訊 - pong:用於回覆meet以及ping資訊,表示已收到,能夠正常通行。此外還能進行群發更新節點狀態 - fail:當其他節點收到fail訊息後立馬把對應節點更新為下線狀態,此時叢集開始進行故障轉移 ![image-20210401204007415](https://images-1302522496.cos.ap-nanjing.myqcloud.com/img/image-20210401204007415.png) # 初步搭建 ## 地址規劃 ​ 3臺伺服器,每臺伺服器開啟2臺例項構建基礎主從。 ​ 伺服器採用centos7.3,Redis版本為6.2.1 ​ 地址規劃與結構圖如下: ![image-20210401213843708](https://images-1302522496.cos.ap-nanjing.myqcloud.com/img/image-20210401213843708.png) ​ 在每個節點hosts檔案中加入以下內容; ``` $ vim /etc/hosts 192.168.0.120 node1 192.168.0.130 node2 192.168.0.140 node3 ``` ​ ## 叢集準備 ​ 為所有節點下載Redis: ``` $ cd ~ $ wget https://download.redis.io/releases/redis-6.2.1.tar.gz ``` ​ 為所有節點配置目錄: ``` $ mkdir -p /usr/local/redis_cluster/redis_63{79,80}/{conf,pid,logs} ``` ​ 所有節點進行解壓: ``` $ tar -zxvf redis-6.2.1.tar.gz -C /usr/local/redis_cluster/ ``` ​ 所有節點進行編譯安裝Redis: ``` $ cd /usr/local/redis_cluster/redis-6.2.1/ $ make && make install ``` ​ 書寫叢集配置檔案,注意!Redis普通服務會有2套配置檔案,一套為普通服務配置檔案,一套為叢集服務配置檔案,我們這裡是做的叢集,所以書寫的叢集配置檔案,共6份: ``` $ vim /usr/local/redis_cluster/redis_6379/conf/redis.cnf # 快速修改::%s/6379/6380/g # 守護進行模式啟動 daemonize yes # 設定資料庫數量,預設資料庫為0 databases 16 # 繫結地址,需要修改 bind 192.168.0.120 # 繫結埠,需要修改 port 6379 # pid檔案儲存位置,檔名需要修改 pidfile /usr/local/redis_cluster/redis_6379/pid/redis_6379.pid # log檔案儲存位置,檔名需要修改 logfile /usr/local/redis_cluster/redis_6379/logs/redis_6379.log # RDB快照備份檔名,檔名需要修改 dbfilename redis_6379.rdb # 本地資料庫儲存目錄,需要修改 dir /usr/local/redis_cluster/redis_6379 # 叢集相關配置 # 是否以叢集模式啟動 cluster-enabled yes # 叢集節點回應最長時間,超過該時間被認為下線 cluster-node-timeout 15000 # 生成的叢集節點配置檔名,檔名需要修改 cluster-config-file nodes_6379.conf ``` # 啟動叢集 ## 啟動叢集 ​ 在啟動叢集時,會按照Redis服務配置檔案的配置項判斷是否啟動叢集模式,如圖所示: ![image-20210401221228929](https://images-1302522496.cos.ap-nanjing.myqcloud.com/img/image-20210401221228929.png) ​ 每個節點上執行以下2條命令進行服務啟動: ``` $ redis-server /usr/local/redis_cluster/redis_6379/conf/redis.cnf $ redis-server /usr/local/redis_cluster/redis_6380/conf/redis.cnf ``` ​ 叢集模式啟動,它的進行後會加上[cluster]的字樣: ``` $ ps -ef | grep redis root 51311 1 0 11:30 ? 00:00:00 redis-server 192.168.0.120:6379 [cluster] root 51329 1 0 11:30 ? 00:00:00 redis-server 192.168.0.120:6380 [cluster] root 51396 115516 0 11:31 pts/1 00:00:00 grep --color=auto redis ``` ​ 同時,檢視一下叢集節點配置檔案,會發現生成了一組叢集資訊,每個Redis服務都是不同的: ``` $ cat /usr/local/redis_cluster/redis_6379/nodes_6379.conf c8a8c7d52e6e7403e799c75302b6411e2027621b :0@0 myself,master - 0 0 0 connected vars currentEpoch 0 lastVoteEpoch 0 $ cat /usr/local/redis_cluster/redis_6380/nodes_6380.conf baa10306639fcaca833db0d521235bc9593dbeca :0@0 myself,master - 0 0 0 connected vars currentEpoch 0 lastVoteEpoch 0 # 第一段資訊是這個Redis服務作為叢集節點的一個身份編碼 # 別名為叢集的node-id ``` ## 加入叢集 ​ 現在雖然說每個服務都成功啟動了,但是彼此之間並沒有任何聯絡。 ​ 所以下一步要做的就是將6個服務加入至一個叢集中,如下操作示例: ``` $ redis-cli -h node1 -p 6379 node1:6379> cluster meet 192.168.0.130 6379 node1:6379> cluster meet 192.168.0.140 6379 node1:6379> cluster meet 192.168.0.120 6380 node1:6379> cluster meet 192.168.0.130 6380 node1:6379> cluster meet 192.168.0.140 6380 ``` ​ 檢視當前叢集所有的節點: ``` node1:6379> cluster nodes 214dc5a10149091047df1c61fd3415d91d6204ea 192.168.0.130:6379@16379 master - 0 1617291123000 1 connected baa10306639fcaca833db0d521235bc9593dbeca 192.168.0.120:6380@16380 master - 0 1617291120000 3 connected 7a151f97ee9b020a3c954bbf78cd7ed8a674aa70 192.168.0.140:6379@16379 master - 0 1617291123000 2 connected bae708f7b8df32edf4571c72bbf87715eb45c169 192.168.0.130:6380@16380 master - 0 1617291124175 4 connected fd1dde2a641727e52b4e82cfb351fe3c17690a17 192.168.0.140:6380@16380 master - 0 1617291124000 0 connected c8a8c7d52e6e7403e799c75302b6411e2027621b 192.168.0.120:6379@16379 myself,master - 0 1617291121000 5 connected ``` ​ 檢視埠監聽,可以發現Gossip監聽的1000+端口出現了,此時代表叢集各個節點之間已經能互相通訊了: ``` $ netstat -lnpt | grep redis tcp 0 0 192.168.0.120:6379 0.0.0.0:* LISTEN 51311/redis-server tcp 0 0 192.168.0.120:6380 0.0.0.0:* LISTEN 51329/redis-server tcp 0 0 192.168.0.120:16379 0.0.0.0:* LISTEN 51311/redis-server tcp 0 0 192.168.0.120:16380 0.0.0.0:* LISTEN 51329/redis-server ``` ## 主從配置 ​ 6個服務之間並沒有任何主從關係,所以現在進行主從配置,記錄下上面cluster nodes命令輸出的node-id資訊,只記錄主節點: | hostname | 節點 | node-id | | -------- | ------------------ | ---------------------------------------- | | node1 | 192.168.0.120:6379 | c8a8c7d52e6e7403e799c75302b6411e2027621b | | node2 | 192.168.0.130:6379 | 214dc5a10149091047df1c61fd3415d91d6204ea | | node3 | 192.168.0.140:6379 | 7a151f97ee9b020a3c954bbf78cd7ed8a674aa70 | ​ 首先是node1的6380,將它對映到node2的6379: ``` $ redis-cli -h node1 -p 6380 node1:6380> cluster replicate 214dc5a10149091047df1c61fd3415d91d6204ea ``` ​ 然後是node2的6380,將它對映到node3的6379: ``` $ redis-cli -h node2 -p 6380 node2:6380> cluster replicate 7a151f97ee9b020a3c954bbf78cd7ed8a674aa70 ``` ​ 最後是node3的6380,將它對映到node1的6379: ``` $ redis-cli -h node3 -p 6380 node3:6380> cluster replicate c8a8c7d52e6e7403e799c75302b6411e2027621b ``` ​ 檢視叢集節點資訊,內容有精簡: ``` $ redis-cli -h node1 -p 6379 node1:6379> cluster nodes 192.168.0.130:6379@16379 master 192.168.0.120:6380@16380 slave 192.168.0.140:6379@16379 master 192.168.0.130:6380@16380 slave 192.168.0.140:6380@16380 slave 192.168.0.120:6379@16379 myself,master # myself表示當前登入的是那個服務 ``` ## 分配槽位 ​ 接下來我們要開始分配槽位了,為了考慮今後的寫入操作能分配均勻,槽位也要進行均勻分配。 ​ 僅在Master上進行分配,從庫不進行分配,僅做備份和讀庫使用。 ​ 使用python計算每個master節點分多少槽位: ``` $ python3 >>> divmod(16384,3) (5461, 1) ``` ​ 槽位分配情況如下,槽位號從0開始,到16383結束,共16384個槽位: | 節點 | 槽位數量 | | ---------- | ------------- | | node1:6379 | 0 - 5461 | | node2:6379 | 5461 - 10922 | | node3:6379 | 10922 - 16383 | ​ 開始分配: ``` $ redis-cli -h node1 -p 6379 cluster addslots {0..5461} $ redis-cli -h node2 -p 6379 cluster addslots {5462..10922} $ redis-cli -h node3 -p 6379 cluster addslots {10923..16383} ``` ​ 檢查槽位是否分配政策,這裡進行內容擷取: ``` $ redis-cli -h node1 -p 6379 node1:6379> CLUSTER nodes 192.168.0.130:6379@16379 master - 0 1617292240544 1 connected 5462-10922 192.168.0.140:6379@16379 master - 0 1617292239000 2 connected 10923-16383 192.168.0.120:6379@16379 myself,master - 0 1617292238000 5 connected 0-5461 # 看master節點的最後 ``` ## 檢查狀態 ​ 使用以下命令檢查叢集狀態,這裡我遇到一個問題,插槽都已經成功分配了但是沒有同步,導致叢集啟動不了: ``` $ redis-cli -h node1 -p 6379 node1: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:5 cluster_stats_messages_ping_sent:2825 cluster_stats_messages_pong_sent:2793 cluster_stats_messages_meet_sent:5 cluster_stats_messages_sent:5623 cluster_stats_messages_ping_received:2793 cluster_stats_messages_pong_received:2830 cluster_stats_messages_received:5623 ``` ## MOVED重定向 ​ 現在我們在node1的master節點上進行寫入: ``` $ redis-cli -h node1 -p 6379 node1:6379> set k1 "v1" (error) MOVED 12706 192.168.0.140:6379 ``` ​ 它會提示你去node2的master上進行寫入。 ​ 這個就是MOVED重定向。 ## -c引數 ​ 如何解決這個問題?其實在登入的時候加上引數-c即可,-c引數無所謂你的Redis是否是叢集模式,建議任何登入操作都加上,這樣即使是Redis叢集也會自動進行MOVED重定向: ``` $ redis-cli -c -h node1 -p 6379 node1:6379> set k1 "v1" -> Redirected to slot [12706] located at 192.168.0.140:6379 OK ``` ​ 一併對主從進行驗證,這條資料是寫入至了node3的Master中,我們登入node2的Slave中進行檢視: ``` $ redis-cli -h node2 -p 6380 -c node2:6380> keys * 1) "k1" ``` # 故障轉移 ## 故障模擬 ​ 模擬node1的6379下線宕機,此時應該由node3的6380接管它的工作: ``` $ redis-cli -h node1 -p 6379 shutdown ``` ​ 登入叢集任意節點檢視目前的叢集節點資訊: ``` node2:6379> cluster nodes 214dc5a10149091047df1c61fd3415d91d6204ea 192.168.0.130:6379@16379 myself,master - 0 1617294532000 1 connected 5462-10922 bae708f7b8df32edf4571c72bbf87715eb45c169 192.168.0.130:6380@16380 slave 7a151f97ee9b020a3c954bbf78cd7ed8a674aa70 0 1617294533000 2 connected # 已下線 c8a8c7d52e6e7403e799c75302b6411e2027621b 192.168.0.120:6379@16379 master,fail - 1617294479247 1617294475173 5 disconnected 7a151f97ee9b020a3c954bbf78cd7ed8a674aa70 192.168.0.140:6379@16379 master - 0 1617294536864 2 connected 10923-16383 # 自動升級為主庫,並且插槽也轉移了 fd1dde2a641727e52b4e82cfb351fe3c17690a17 192.168.0.140:6380@16380 master - 0 1617294536000 6 connected 0-5461 baa10306639fcaca833db0d521235bc9593dbeca 192.168.0.120:6380@16380 slave 214dc5a10149091047df1c61fd3415d91d6204ea 0 1617294535853 1 connected ``` ## 恢復工作 ​ 重啟node1的6379: ``` $ redis-server /usr/local/redis_cluster/redis_6379/conf/redis.cnf ``` ​ 登入node1的6379,發現他已經自動的進行上線了,並且作為node3中6380的從庫: ``` $ redis-cli -h node1 -p 6379 node1:6379> cluster nodes # 自動上線 c8a8c7d52e6e7403e799c75302b6411e2027621b 192.168.0.120:6379@16379 myself,slave fd1dde2a641727e52b4e82cfb351fe3c17690a17 0 1617294746000 6 connected ``` # cluster命令 ​ 以下是叢集中常用的可執行命令,命令執行格式為: ``` cluster 下表命令 ``` ​ 命令如下,未全,如果想了解更多請執行cluster help操作: | 命令 | 描述 | | -------------------------------- | ------------------------------------------------------------ | | INFO | 返回當前叢集資訊 | | MEET \ \ [\] | 新增一個節點至當前叢集 | | MYID | 返回當前節點叢集ID | | NODES | 返回當前節點的叢集資訊 | | REPLICATE \ | 將當前節點作為某一叢集節點的從庫 | | RESET [HARD\|SOFT] | 重置當前節點資訊 | | ADDSLOTS \ [\ ...] | 為當前叢集節點增加一個或多個插槽位,推薦在bash shell中執行,可通過{int..int}指定多個插槽位 | | DELSLOTS \ [\ ...] | 為當前叢集節點刪除一個或多個插槽位,推薦在bash shell中執行,可通過{int..int}指定多個插槽位 | | FLUSHSLOTS | 刪除當前節點中所有的插槽資訊 | | FORGET \ | 從叢集中刪除某一節點 | | COUNT-FAILURE-REPORTS \ | 返回當前叢集節點的故障報告數量 | | COUNTKEYSINSLOT \ | 返回某一插槽中的鍵的數量 | | GETKEYSINSLOT \ \ | 返回當前節點儲存在插槽中的key名稱。 | | KEYSLOT \ | 返回該key的雜湊槽位 | | SAVECONFIG | 儲存當前叢集配置,進行落盤操作 | | SLOTS | 返回該插槽的資訊 |