前言
哨兵模式雖然讓讀寫分離更加高可用,但單台服務器由於本身的內存和CPU瓶頸,對於高並發和大數據業務的應用場景還是遠遠不能滿足;對於這種情況,有點經驗的小伙伴會毫不猶豫的想到集群,搞他好幾個節點,負載均衡再加上故障轉移,豈不美哉。是的,就是這個理,接下來玩玩。
正文
集群,相信這個詞小伙伴應該聽的耳朵起繭子了吧;多搞幾台服務器,讓請求/命令平均分發到各個服務器,避免單台服務器承載過大壓力;對於Redis集群來說,為了實現自動故障轉移,還需要在每個主節點上增加一個或多個從節點,當主節點發生故障時,從節點自動補上,實現高可用。
總的來說,Redis集群有以下作用:
- 多主節點的實現可以應對高並發場景,並發量增大,節點可以隨時擴展滿足需求;
- 多主節點的實現可以存儲更多的數據,因為數據均勻分布到各個節點;
- 多主節點搭配多從節點的實現讓高可用更加穩定,即當有主節點發生故障時,對應下面的從節點會升級為主節點,正常提供功能;
老規矩不變,一邊實操一邊總結,接下來搭建一個3主3從的集群,這是最簡單的。 Redis集群中最少需要3個主節點,再加上為了實現高可用,每個主節點至少得跟一個從節點,不然一個主節點掛了,找不到完整的數據,整個集群就不能用了;至於為什么會找不到完整的數據,下面會聊到。
接下來要搭建的集群環境如下:
簡要說明:
- 6370為主節點,6381為6370的從節點;
- 6380為主節點,6391為6380的從節點;
- 6390為主節點,6371為6390的從節點;
- 在集群環境中主節點之間是相互通訊的(這里沒有哨兵),每一個節點都是數據節點;
這里集群方案使用redis-cli自動指定主從關系(小伙伴的主從關系可能會和我這不一樣哦),也可以手動指定;反正思路都一樣;
以下演示在同一台機器上,通過端口區分各個節點;在實際開發中,一般都是用不同的服務器。
案例演示
-
准備六個節點的配置文件,開啟集群相關配置;
拷貝最初默認的配置文件,然后進行更改,主要更改以下項:
port 6370 # 指定Redis節點端口 pidfile /var/run/redis_6370.pid # 指定對應進程文件 dbfilename dump6370.rdb # 每個節點的rdb持久化文件 cluster-enabled yes # 開啟集群,這個比較重要 cluster-config-file nodes-6370.conf #指定每個節點的集群配置文件,這個比較重要
以上配置文件內容在其他節點(6370,6371,6380,6381,6390,6391)都需要進行修改,只是將其中6370改為對應節點的端口即可,目的就是為了不同節點使用不同端口並區分用到的不同文件即可;比如需要修改6371節點的配置文件如下:
port 6371 # 指定Redis節點端口 pidfile /var/run/redis_6371.pid # 指定對應進程文件 dbfilename dump6371.rdb # 每個節點的rdb持久化文件 cluster-enabled yes # 開啟集群,這個比較重要 cluster-config-file nodes-6371.conf #指定每個節點的集群配置文件,這個比較重要
其中cluster-enabled和cluster-config-file是集群配置的重點。
-
啟動六個節點,剛開始各個節點是相互獨立的;
准備好配置文件之后,就可以使用redis-server指定配置文件啟動節點啦,如果節點多,小伙伴可以編寫腳本哦;
./redis-server ZoeCluster/redis6370.conf # 啟動6370節點 ./redis-server ZoeCluster/redis6371.conf # 啟動6371節點 ./redis-server ZoeCluster/redis6380.conf # 啟動6380節點 ./redis-server ZoeCluster/redis6381.conf # 啟動6381節點 ./redis-server ZoeCluster/redis6390.conf # 啟動6390節點 ./redis-server ZoeCluster/redis6391.conf # 啟動6391節點
啟動效果如下:
這樣只是將各個節點啟動起來,集群關系還沒創建呢,如果不信,可以使用redis-cli連接任意一個節點查看集群信息,如下:
如上圖,cluster-size為0,集群的關鍵,槽也還沒有分配;那接下來肯定是要將各節點的集群關系搞起來;
-
建立節點集群關系
由於我使用的Redis版本是5.0,直接可以使用redis-cli就可以進行集群搭建,在此版本之前都推薦使用redis-trib.rb進行相關操作,這個是一個Ruby腳本,需要安裝相關環境,小伙伴可以下來嘗試;
使用命令如下:
./redis-cli --cluster create --cluster-replicas 1 127.0.0.1:6370 127.0.0.1:6380 127.0.0.1:6390 127.0.0.1:6371 127.0.0.1:6381 127.0.0.1:6391
參數簡介:
-- cluster : 指定是用於創建集群環境;
-a:密碼,即如果有密碼,可以通過-a傳參,這里沒有設置密碼;
--cluster-replicas:這里設置為1, 用於配置主節點上的從節點數,1就代表一主一從,2就代表一主二從,依次類推;
后面的是節點IP:節點端口,一般前面的是主節點,后面的是從節點;
如果同意集群方案,然后就開始進行相關操作,如下:
同樣,可以連接到任意一個節點,查看集群情況,如下:
注:關於主從節點之間的主從復制過程就不在這說了,和之前說過的主從復制一樣;
-
演示訪問操作;
使用redis-cli連接任意節點寫入數據,如果不指定集群連接的話,會寫入數據失敗,如下:
由於集群環境下,數據的存儲位置是根據Key來計算而來的(這里牽涉到一致性哈希算法),使得數據可以均勻分配到各節點上,所以在redis-cli連接的時候需要指定集群模式,如下:
如上圖所示,指定集群模式之后就可以正常存取了;
-
故障演示
既然集群環境,肯定少不了要好好測測;
模擬從節點掛掉
找個從節點停掉試試,這里停掉6371節點,根據創建集群信息知道,它的主節點是6390;
主節點顯示從節點斷開連接,看看它的主節點反應:
其他集群節點只是將從節點標記為failing狀態,即下線狀態,如下:
對於存取數據也不受影響,這里就不截圖了,小伙伴自行嘗試吧;
當故障的從節點6371重新連上時,主節點恢復主從關系,並進行主從復制操作;其他集群節點會清除原來標記的下線狀態,將其改為上線;
模擬主節點掛掉
這里就手動將6390這個節點停掉,會有怎樣的反應呢?
自身從節點會每隔一秒檢測連接,如果超時(默認是15秒),會選舉從節點做為集群的主節點來提供服務,如下圖:
數據存取最終還是不受影響;
對於其他集群節點,將故障節點標記為failing,讓新上任的主節點提供服務,如下圖:
存取數據也是不受影響的;
掛掉的主節點6390如果恢復,那它只能變為6371的從節點啦,並進行相關主從復制操作;而集群的其他節點只是將其原有的Fail狀態清除,表示可以正常連接;
Redis集群就是這樣簡單,只要思路對,就是手工活;小伙伴可以編寫腳本自動執行哦;
接下來說說集群數據的存儲;
數據存儲簡單分析
在Redis集群環境中,數據的存儲位置是根據對Key的Hash計算進行指定的; Redis集群為了在節點改變時保證數據分布均勻,引入了槽(slot)作為遷移的基本單位,槽解耦了數據和實際節點的關系,使得實際節點數的改變對系統影響較小;
在整個集群中,槽(slot)總共有16384,會將其均勻分配到集群的主節點上,其中每一份槽對應一個存儲空間(這里的存儲空間可以理解為一個容器,是可以存很多數據的),以上集群環境的槽分配如下:
存儲數據的過程,如下:
連接6380主節點,執行如下操作:
具體過程如下:
簡要說明:
- 客戶端發起命令;
- 服務器將Key進行CRC16計算,並與總槽位計算出Key需要存儲的位置;
- 這里模擬的Key為zoe,計算出的槽位為14588,不在6380這個節點上,集群節點會將其重定向到對應槽位的節點上;
- 然后找到6371上的14588槽位進行數據存儲;(注,這里的6371已經是主節點了,因為上面做過一次故障轉移模擬);
那集群節點是如何知道其他節點的槽范圍和其他信息呢?
那是因為各節點之間有通訊,通訊端口是對應的redis端口+10000,比如節點6371的集群通訊端口為16371(如果多台機器,別忘了防火牆放開這個端口哦),可以通過cluster nodes看到,如下:
並且將各節點的信息保存在自己對應的集群配置文件中,這個集群文件名是通過配置項cluster-config-file指定的,在集群節點啟動時會檢查該文件是否存在,如果不存在,會自動創建,如果存在,就加載里面的相關配置信息;里面有哪些信息,隨便找個節點的配置文件看一下:
如上圖所示,各節點的配置文件中記錄了其他節點的主從關系,分配的槽位,各節點的狀態;這樣的話,集群關系就算重新啟動也還存在。
集群伸縮(節點增刪)演示
在實際應用場景中,會根據業務需要,對集群進行伸縮,即節點的增刪;業務並發大了加節點進行擴展, 節點需要調整時可能需要進節點刪除;
加節點
這里進行節點擴展,加一個6360主節點,6361作為6360的從節點;參照以上集群搭建時配置文件更改,然后將其都啟動,如下:
6360節點
port 6360 # 指定Redis節點端口
pidfile /var/run/redis_6360.pid # 指定對應進程文件
dbfilename dump6360.rdb # 每個節點的rdb持久化文件
cluster-enabled yes # 開啟集群,這個比較重要
cluster-config-file nodes-6360.conf #指定每個節點的集群配置文件,這個比較重要
6361節點
port 6361 # 指定Redis節點端口
pidfile /var/run/redis_6361.pid # 指定對應進程文件
dbfilename dump6361.rdb # 每個節點的rdb持久化文件
cluster-enabled yes # 開啟集群,這個比較重要
cluster-config-file nodes-6361.conf #指定每個節點的集群配置文件,這個比較重要
兩個節點都啟動,然后將6360加入到集群主節點中,執行以下命令:
./redis-cli --cluster add-node 127.0.0.1:6360 127.0.0.1:6370
注:其中127.0.0.1:6360是需要加入的新增節點,127.0.0.1:6370是現有集群中的任意一個節點;
可以通過以下命令檢測集群狀態,如下:
./redis-cli --cluster check 127.0.0.1:6370 # 后面的地址是任意的集群節點
可以看到6360已經加入到集群環境中,但現在還沒有從節點和槽分配,所以接下來先將6361作為6360的從節點加入,如下:
./redis-cli --cluster add-node --cluster-slave --cluster-master-id eaa814dc56beb0d5edb6a4fbb14f1384e78d4764 127.0.0.1:6361 127.0.0.1:6370
參數說明:
- --cluster-slave : 意思就是加入的是從節點;
- --cluster-master-id:后面緊跟主節點的id,這里就是6360的節點id;通過cluster nodes可以查看到節點id;
- 127.0.0.1:6361:需要加入的從節點;
- 127.0.0.1:6370:現有集群的任意主節點;
現在還差槽分配了,如果需要直接將16384個槽平均分配到所有節點話,直接執行以下命令即可:
./redis-cli --cluster rebalance --cluster-threshold 1 --cluster-use-empty-masters 127.0.0.1:6370
使用命令./redis-cli --cluster check 127.0.0.1:6370查看分配結果,如下圖,只截了部分:
如果不想均勻分配,根據自定義需要進行配置,可以執行以下命令,會提示一步一步配置;
./redis-cli --cluster reshard 127.0.0.1:6360 #后面是新加入的主節點
# 也可以執行以下指令直接配置想要的數據
./redis-cli --cluster reshard --cluster-from all --cluster-to 需要分配槽的節點id --cluster-slots 1000 --cluster-yes 127.0.0.1:6370 # 1000 指分配的槽數
這里就不截圖演示了,留給小伙伴自己動手操作吧;
刪除節點
-
先對節點進行分片工作,防止數據丟失,即將指定節點上的槽分配到其他節點;
./redis-cli --cluster reshard 要刪除節點ip:port
-
移除節點,推薦先刪除從節點,再刪除主節點;
./redis-cli --cluster del-node 節點ip:port 節點id
集群配置項
- cluster-enabled(是否開啟集群模式):設置為yes,將該節點開啟為集群模式;
- cluster-config-file(設置每個集群節點對應的配置文件名稱):文件是自動生成的,不用手動創建;
- cluster-node-timeout(設置超時時間,即集群節點不可用的最大時間,如果超過這個時間就認為該節點不可用):默認為15000(以毫秒為單位);
- cluster-migration-barrier(配置一個主機最少可用的從機的個數):默認是1,表示一個主機的從機遷移之后,至少得有一個從機可用,否則不進行節點遷移;
- cluster-require-full-coverage(配置集群服務的可用性):默認yes開啟,即集群沒完全覆蓋所有slot,集群就掛了;設置為no,就算槽沒有全分配,也能提供服務,需要自己保證槽分配;
總結
到這集群的搭建就完啦,本來想着寫着很簡單的,沒想到又干了4000字;對於集群,使用有一些限制,比如Keys命令只能針對當前節點,需要針對多節點的情況進行處理;集群中各節點只支持db0數據庫,其他數據庫不支持等等;所以使用要注意哦,后續抽時間單獨整理一篇注意事項吧,篇幅有點長,不繼續聊啦; 下篇說說熟悉的緩存穿透、緩存擊穿、緩存雪崩吧;
一個被程序搞丑的帥小伙,關注"Code綜藝圈",跟我一起學~~~