一.概述 Redis集群提供了分散式資料庫方案,集群通過分片來進行數據共用,並提供複製和故障轉移功能。在大數據量方面的高可用方案,cluster集群比Sentinel有優勢。但Redis集群並不支持處理多個keys的命令,因為這需要在不同的節點間移動數據,而達不到像Redis那樣的性能,在高負載的情 ...
一.概述
Redis集群提供了分散式資料庫方案,集群通過分片來進行數據共用,並提供複製和故障轉移功能。在大數據量方面的高可用方案,cluster集群比Sentinel有優勢。但Redis集群並不支持處理多個keys的命令,因為這需要在不同的節點間移動數據,而達不到像Redis那樣的性能,在高負載的情況下可能會導致不可預料的錯誤。學習集群同樣先瞭解一些原理方麵包括:節點、槽指派、命令執行、重新分片,轉向、故障轉移、消息。後面再操作集群演示。關於集群搭建後面會列出實現步驟,也可參考Redis官網的實現步驟:http://www.redis.cn/topics/cluster-tutorial.html
1.1 節點
一個Redis集群通常由多個節點(node)組成,開始每個節點都是相互獨立的,需要將獨立的節點連接起來,構成一個包含多節點的集群。連接各節點的工作使用cluster meet命令來完成,格式如下:
cluster meet ip port
向一個節點發送該命令,可以讓發送的節點與指定的節點進行握手,握手成功時,指定的節點就會添加到發送節點當前所在的集群中。例如:有三個獨立的節點,埠分別為:7000, 7001,7002。首先使用客戶端連接上節點7000, 通過發送cluster nodes命令可以看到,集群目前只包含7000自己一個節點,通過cluster nodes查看信息如下:
/usr/local/redis/bin [root@hsr bin]# ./redis-cli -c -p 7000 -a 123456 127.0.0.1:7000> cluster nodes 142116fa16006f39865ebe604d1580c119fa0fea :7000@17000 myself,master - 0 0 0 connected
通過向節點7000發送以下命令,可以將節點7001 添加到7000所在的集群中:
127.0.0.1:7000> cluster meet 127.0.0.1 7001 OK
繼續向節點7000發送以下命令,可以將節點7002也添加到7000和7001所在的集群中:
127.0.0.1:7000> cluster meet 127.0.0.1 7002 OK
三個節點握手成功,使三個節點都處於同一個集群中,再次查看集群節點情況,三個節點都connected連接成功:
127.0.0.1:7000> cluster nodes 0eed9cc9122d2724365550b70965c2a8e281043d 127.0.0.1:7002@17002 master - 0 1545632759414 2 connected aeaaeacb8b4d4c4a3bca3c6f52fc4b363e68f083 127.0.0.1:7001@17001 master - 0 1545632758405 0 connected 142116fa16006f39865ebe604d1580c119fa0fea 127.0.0.1:7000@17000 myself,master - 0 1545632756000 1 connected
cluster nodes由以下欄位組成,表格如下:
欄位名 |
描述 |
節點7002的欄位值 |
id |
節點 ID,一個40個字元的隨機字元串 |
0eed9cc9122d2724365550b70965c2a8e281043d |
ip:port | 客戶端應該聯繫節點以運行查詢的節點地址 | 127.0.0.1:7002 |
flags | 逗號列表分隔的標誌,myself,master,slave,fail等 | master |
master | 如果節點是從屬節點,並且主節點已知,則節點ID為主節點,否則為“ - ”字元 | - |
ping-sent | 毫秒為單位的當前激活的ping發送的unix時間 | 1545632759414 |
config-epoch | 當前節點的配置時期,每次發生故障切換時,都會創建一個新的,唯一的 | 2> |
link-state | 用於節點到節點集群匯流排的鏈路狀態。我們使用此鏈接與節點進行通信 | connected |
slot | 在connected後面還會顯示槽號範圍 | 分配槽後顯示 |
(1) 啟動節點
一個節點就是一個運行在集群模式下的Redis伺服器,Redis伺服器在啟動時會根據cluster-enabled 配置選項是否為yes來決定是否開啟伺服器的集群模式。每個節點還是與普通redis服務一樣,具備所有功能比如:支持RDB、AOF持久化、發佈與訂閱、保存鍵值對、複製 等等。對於集群模式才會用到的數據,內部保存在cluster.h/clusterState結構中。
(2) 集群數據結構
內部clusterNode結構保存了一個節點的當前狀態,比如節點的創建時間、節點的名字、節點當前的配置紀元、節點的IP、Port等等。每個節點都會使用一個clusterNode結構來記錄自己的狀態,併為集群中的所有其它節點(包括主從)都會創建一個相應的clusterNode結構, 以此來記錄其它節點的狀態。該結構中的屬性不再介紹,詳細請查看"redis設計與實現"書。
(3) cluster meet 命令的實現
上面講到通過cluster meet 命令,可以使節點與指定的節點進行握手形成集群。假設有節點A和B 握手的步驟包括:
(1) 在客戶端向節點A發送一條 cluster meet B命令時,節點A會為節點B創建一個ClusterNode結構,並將該結構添加到自己的ClusterState.nodes字典中。
(2) 向節點B發送meet後,如果順利節點B將接收到節點A消息,節點B會為節點A創建一個ClusterNode結構, 並將該結構添加到自己的ClusterState.nodes字典中。
(3) 節點B 將向節點A返回一條PONG消息。
(4) 節點A接收B返回的消息,能過這條PONG消息節點A知道節點B已經成功接收到了自己發送的MEET消息。
(5) 節點A將向B返回一條PING消息。
(6) 節點B將接收A的消息,通過這條PING消息節點B知道節點A已經成功接收到了自己返回的PONG消息,握手成功。
之後節點A會將節點B的信息通過Gossip協議傳播給集群中的其他節點,讓其他節點也與節點B進行握手,最終節點B會被集群中所有節點認識。
1.2 槽指派
Redis集群通過分片的方式來保存資料庫中的鍵值對:集群的整個 資料庫被分為16384個槽(slot), 資料庫中的每個鍵都屬於這16384 個slot其中一個,集群中的每個節點可以處理0個或最多16384個slot。當資料庫中的16384個slot都有在節點中分配時,此時集群處於上線狀態(OK),相反如果任何一個slot沒有得到分配,那麼集群處於下線狀態(fail)。
在節點7000的客戶端通過cluster info可以查看,因為集群中的三個節點都沒有在處理任何slot,所以處於下線狀態。能過cluster info來查看集群為fail狀態,如下所示:
127.0.0.1:7000> cluster info cluster_state:fail cluster_slots_assigned:0 cluster_slots_ok:0 cluster_slots_pfail:0 cluster_slots_fail:0 cluster_known_nodes:3
通向節點發送cluster addslots命令,可以將一個或多個slot指派給某節點負責。例如將slot 0 到5000指派給節點7000負責,手動添加槽時,一定要退出redis客戶端,命令如下:
[root@hsr bin]# ./redis-cli -h 127.0.0.1 -p 7000 -a 123456 cluster addslots {0..5000} OK
在登到redis客戶端,查看集群節點的槽分配情況,可以看到7000節點已分配了0-5000的槽範圍:
[root@hsr bin]# ./redis-cli -c -p 7000 -a 123456 127.0.0.1:7000> cluster nodes 0eed9cc9122d2724365550b70965c2a8e281043d 127.0.0.1:7002@17002 master - 0 1545635637429 2 connected aeaaeacb8b4d4c4a3bca3c6f52fc4b363e68f083 127.0.0.1:7001@17001 master - 0 1545635638436 0 connected 142116fa16006f39865ebe604d1580c119fa0fea 127.0.0.1:7000@17000 myself,master - 0 1545635635000 1 connected 0-5000
為了讓7000、7001、7002三個節點所在的集群進入上線狀態,繼續將slot 5001~10000指派給節點7001。 將slot 10001~16383指派給7002。當三個cluster addslots 命令都執行後,資料庫中的16383個slot都已經指派給了相應的節點,集群進入上線狀態。
[root@hsr bin]# ./redis-cli -h 127.0.0.1 -p 7001 -a 123456 cluster addslots {5001..10000} OK [root@hsr bin]# ./redis-cli -h 127.0.0.1 -p 7002 -a 123456 cluster addslots {10001..16383} OK
此時已經進入集群進入上線狀態:如下所示:
127.0.0.1:7000> cluster info cluster_state:ok cluster_slots_assigned:16384 cluster_slots_ok:16384 cluster_slots_pfail:0 cluster_slots_fail:0 cluster_known_nodes:3 cluster_size:2
127.0.0.1:7000> cluster nodes a9e82a7870ac31c221a4d13b28ba9897bb12257c 127.0.0.1:7000@17000 myself,master - 0 1545704528000 0 connected 0-5000 3b10786d21bbeb66e3517e8d3daa3ee2ce16705e 127.0.0.1:7001@17001 master - 0 1545704529806 1 connected 5001-10000 7bd0cbd26392d1e98ffe9d46ae153c944d8f398d 127.0.0.1:7002@17002 master - 0 1545704529000 2 connected 10001-16383
一個節點除了會將自己負責處理的slot外,還會將自己的slot數組通過消息發送給集群中的其他節點,以此來告知其他節點自己目前處理哪些slot。例如:當節點A 通過消息從節點B那裡接收到節點B的slot數組時,節點A會在自己的ClusterState.nodes字典中查找節點B對應的ClusterState結構,並對結構中的slots數組進行保存或更新。這樣下來集群中的每個節點都會知道資料庫中的16384個slot分別被指派給了集群中的哪些節點。
下圖是集群中各節點的ClusterState.nodes字典,裡面記錄了slot與各節點的分配關係:
1.3 在集群中執行命令
在對資料庫中的16384個slot都進行了指派之後,集群進入上線狀態,這時客戶端就可以向集群中的節點發送數據命令了。當客戶端向節點發送鍵有關的命令時,接收的節點會計算出命令要處理的鍵屬於哪個slot,並檢查這個slot是否派給了節點自己:
(1)如果鍵所在的slot正好就指派給了當前節點,那麼節點直接執行這個命令。
(2)如果鍵所在的slot並沒有指派給當前節點,那麼節點會向客戶端返回一個moved錯誤,指引客戶端轉向到正確的節點,並再次發送之前想要的執行命令。
自動分配節點和槽號,示例如下所示:
-- 在節點7000寫入一個鍵值對,發現自動分配到7001的14041槽號中。 127.0.0.1:7000> set cluster "hello 7000" -> Redirected to slot [14041] located at 127.0.0.1:7001 OK --節點7000,讀取該鍵,自動重定向到7001節點中出讀取 127.0.0.1:7000> get cluster -> Redirected to slot [14041] located at 127.0.0.1:7001 "hello 7000" --節點7000, 修改該鍵,自動重定向到7001節點中出修改 127.0.0.1:7000> set cluster "hello 7001" -> Redirected to slot [14041] located at 127.0.0.1:7001 OK
(1) 計算鍵屬於哪個slot槽
redis 集群共有16384個哈希槽, 每個key通過CRC16校驗後對16384個槽取模來決定放置到哪個節點的槽號。使用cluster keyslot key命令可以查看一個指定的鍵屬於哪個slot,例如下麵讀取cluster鍵,對應槽號為14041。如下所示:
127.0.0.1:7000> cluster keyslot cluster (integer) 14041
(2) moved錯誤(Redirected重定向 )
當節點發現鍵所在的槽並非由自己負責處理的時候,節點就會向客戶端返回一個moved錯誤,指引客戶端轉向到正在負責槽的節點。moved錯誤格式為:moved slot ip :port。 其中slot為鍵所在的槽,而ip和port則是負責處理槽slot的節點的ip地址和埠號。
在集群模式下客戶端接收到moved錯誤時,並不會列印出moved錯誤,而是根據moved錯誤自動進行節點轉向,並列印出轉嚮日志Redirected to slot 信息。只有在單機模式下,才會列印moved信息,因為單機模式下的redis-cli客戶端不清楚moved錯誤的作用,所以會直接將moved信息列印出來,則不是進行節點轉向。
(3) 節點資料庫的實現
集群節點保存鍵值對以及鍵值對 過期的方式與單機redis服務方式完全相同,節點與單機伺服器在資料庫方面的一個區別是:節點只能使用0號資料庫,而單機redis服務則沒有這一限制。
1.4 重新分片
Redis集群的重新分片操作可以將任意數量已經指派給某個節點(源節點)的槽改為指派給另一個節點,並且相關槽所屬的鍵值對也會從源節點被移動到目標節點。重新分片操作可以線上online進行,重新分片過程中,集群不需要下線,並且源節點和目標節點都可以繼續處理命令請求。
例如:在原有節點7000,7001,7002三個節點的集群上,新添加一個節點,埠為7003,然後通過重新分片操作,將原本指派給節點7002的10001~16383的槽,將其中的14042-16383重新分給節點7003。
-- 配置好7003的redis.conf, 啟動7003的redis服務 [root@hsr cluster-test]# pwd /usr/local/redis/cluster-test [root@hsr cluster-test]# ./redis-server ./7003/redis.conf -- 將7003加入集群中 127.0.0.1:7000> cluster meet 127.0.0.1 7003 OK -- 查看集群信息時,有兩個槽(4808,14041)已經自動分配到了7003節點 127.0.0.1:7000> cluster nodes 399051ed127fbd1df8a0455858da9c103bf4864a 127.0.0.1:7003@17003 master - 0 1545705387000 3 connected 4808 14041 a9e82a7870ac31c221a4d13b28ba9897bb12257c 127.0.0.1:7000@17000 myself,master - 0 1545705386000 0 connected 0-4807 4809-5000 3b10786d21bbeb66e3517e8d3daa3ee2ce16705e 127.0.0.1:7001@17001 master - 0 1545705387741 1 connected 5001-10000 7bd0cbd26392d1e98ffe9d46ae153c944d8f398d 127.0.0.1:7002@17002 master - 0 1545705387000 2 connected 10001-14040 14042-16383
通過setslot來簡單演示下重新分配槽:
在遷移(目的節點)執行cluster setslot <slot> IMPORTING <node ID>
命令,指明需要遷移的slot和遷移源節點。
下麵登錄到7003目的節點,使用cluster setslot將參數14042槽號以及所在的源節點ID,導入到當前7003節點中。
127.0.0.1:7003> cluster setslot 14042 importing 7bd0cbd26392d1e98ffe9d46ae153c944d8f398d OK -- 查看cluster nodes ,7003節點信息如下: 399051ed127fbd1df8a0455858da9c103bf4864a 127.0.0.1:7003@17003 myself,master - 0 1545707846000 3 connected 4808 14041
[14042-<-7bd0cbd26392d1e98ffe9d46ae153c944d8f398d]
取消遷移操作,可在遷移源節點和遷移目的節點上執行cluster setslot <slot> STABLE
。
下篇介紹使用redis-trib.rb來進行重新分片,redis-trib.rb自動實現了setslot
的
完整的遷移流程。