Redis Cluster介紹

1. Redis Cluster介紹

Redis ClusterRedis的分佈式解決方案,在Redis 3.0版本正式推出的,有效解決了Redis分佈式方面的需求。當遇到單機內存、併發、流量等瓶頸時,可以採用Cluster架構達到負載均衡的目的。

1.1 數據分佈理論

分佈式數據庫首要解決把整個數據集按照分區規則映射到多個節點的問題,即把數據集劃分到多個節點上,每個節點負責整個數據的一個子集。常見的分區規則有哈希分區順序分區Redis Cluster採用哈希分區規則,因此接下來會討論哈希分區規則。常見的哈希分區有以下幾種:

  • 節點取餘分區
  • 一致性哈希分區
  • 虛擬槽分區

Redis Cluster採用虛擬槽分區,因此先介紹一下虛擬槽分區。

虛擬槽分區巧妙地使用了哈希空間,使用分散度良好的哈希函數把所有的數據映射到一個固定範圍內的整數集合,整數定義爲槽(slot)。比如Redis Cluster槽的範圍是0 ~ 16383。槽是集羣內數據管理和遷移的基本單位。採用大範圍的槽的主要目的是爲了方便數據的拆分和集羣的擴展,每個節點負責一定數量的槽。

Redis-Cluster採用無中心結構,每個節點保存數據和整個集羣狀態,每個節點都和其他所有 節點連接。其redis-cluster架構圖如下:

這裏寫圖片描述

其結構特點:

  • 一般來講,一個主節點至少都要配一個從節點。
  • 所有的redis節點彼此互聯(PING-PONG機制),內部使用二進制協議優化傳輸速度和帶寬。包括從節點也會接收Gossip消息。
  • 節點的fail是通過集羣中超過半數的節點檢測失效時才生效。
  • 客戶端與redis節點直連,不需要中間proxy層。客戶端不需要連接集羣所有節點,連接集羣中任何一個可用節點即可。
  • redis-cluster把所有的物理節點映射到[0-16383]slot上(不一定是平均分配),cluster 負責維護node、slot、value之間的對應關係。
  • Redis集羣預分好16384個桶,當需要在 Redis 集羣中放置一個 key-value 時,根據 CRC16(key) mod 16384的值,決定將一個key放到哪個桶中。

1.2 Redis 數據分區

Redis Cluster採用虛擬槽分區,所有的鍵根據哈希函數映射到0 ~ 16383,計算公式:slot = CRC16(key)&16383。每一個節點負責維護一部分槽以及槽所映射的鍵值數據。

下圖展現一個五個節點構成的集羣,每個節點平均大約負責3276個槽,以及通過計算公式映射到對應節點的對應槽的過程。

這裏寫圖片描述

Redis虛擬槽分區的特定:

  • 解耦數據和節點之間的關係,簡化了節點擴容和收縮難度。
  • 節點自身維護槽的映射關係,不需要客戶端或者代理服務維護槽分區元數據。
  • 支持節點、槽、鍵之間的映射查詢,用於數據路由、在線伸縮等場景。

1.3 Redis 集羣功能限制

Redis集羣相對單機在功能上有一定限制。

  • key批量操作支持有限。如:MSET``MGET,目前只支持具有相同slot值的key執行批量操作。
  • key事務操作支持有限。支持多key在同一節點上的事務操作,不支持分佈在多個節點的事務功能。
  • key作爲數據分區的最小粒度,因此不能將一個大的鍵值對象映射到不同的節點。如:hashlist
  • 不支持多數據庫空間。單機下Redis支持16個數據庫,集羣模式下只能使用一個數據庫空間,即db 0
  • 複製結構只支持一層,不支持嵌套樹狀複製結構。

2. 搭建 Redis Cluster

搭建集羣工作分爲三步:

  • 準備節點
  • 節點握手
  • 分配槽

2.1 準備節點

Redis 集羣一般由多個節點組成,節點數量爲6個才能保證組成完整高可用的集羣。下面給出一個節點的配置,其他的節點和該節點只是端口不同。

port 6379                               //端口
cluster-enabled yes                     //開啓集羣模式
cluster-config-file nodes-6379.conf     //集羣內部的配置文件
cluster-node-timeout 15000              //節點超時時間,單位毫秒
// 其他配置和單機模式相同12345

啓動所有的節點

sudo redis-server conf/redis-6384.conf
sudo redis-server conf/redis-6383.conf
sudo redis-server conf/redis-6382.conf
sudo redis-server conf/redis-6381.conf
sudo redis-server conf/redis-6380.conf
sudo redis-server conf/redis-6379.conf123456

可以查看日誌文件

cat log/redis-6379.log
13103:M 30 May 15:02:09.577 * DB loaded from disk: 0.000 seconds
13103:M 30 May 15:02:09.578 * The server is now ready to accept connections on port 6379123

有日誌文件可得,節點已經啓動成功。這個日誌文件是Redis服務器普通的日誌文件,在集羣模式下,第一次也會自動創建一個日誌文件,由配置文件cluster-config-file指定文件。

集羣配置文件的作用:當集羣內節點發生信息變化時,如添加節點、節點下線、故障轉移等。節點會自動保存集羣的狀態到配置文件中。該配置文件由Redis自行維護,不要手動修改,防止節點重啓時產生集羣信息錯亂。我們來查看一下,集羣模式的日誌文件:

cat nodes-6379.conf 
29978c0169ecc0a9054de7f4142155c1ab70258b :0 myself,master - 0 0 0 connected
vars currentEpoch 0 lastVoteEpoch 0123

也可以通過客戶端連接該節點,通過命令CLUSTER NODES來查看:

127.0.0.1:6379> CLUSTER NODES
29978c0169ecc0a9054de7f4142155c1ab70258b :6379 myself,master - 0 0 0 connected12

2.2 節點握手

節點握手是指一批運行在集羣模式的節點通過Gossip協議彼此通信,達到感知對方的過程。節點握手是集羣彼此通信的第一步,由客戶端發起命令:cluster meet <ip> <port>

127.0.0.1:6379> CLUSTER MEET 127.0.0.1 6380
OK
// 發送CLUSTER NODES可以查看到已經感知到 6380 端口的節點了。
127.0.0.1:6379> CLUSTER NODES
29978c0169ecc0a9054de7f4142155c1ab70258b 127.0.0.1:6379 myself,master - 0 0 1 connected
8f285670923d4f1c599ecc93367c95a30fb8bf34 127.0.0.1:6380 master - 0 1496129041442 0 connected123456

讓所有的節點都互相感知:

127.0.0.1:6379> CLUSTER MEET 127.0.0.1 6381
OK
127.0.0.1:6379> CLUSTER MEET 127.0.0.1 6382
OK
127.0.0.1:6379> CLUSTER MEET 127.0.0.1 6383
OK
127.0.0.1:6379> CLUSTER MEET 127.0.0.1 6384
OK
// 已經全部感知到所有的節點
127.0.0.1:6379> CLUSTER NODES
e0c7961a1b07ab655bc31d8dfd583da565ec167d 127.0.0.1:6384 master - 0 1496129143703 0 connected
961097d6be64ebd2fd739ff719e97565a8cee7b5 127.0.0.1:6382 master - 0 1496129141678 0 connected
29978c0169ecc0a9054de7f4142155c1ab70258b 127.0.0.1:6379 myself,master - 0 0 1 connected
8f285670923d4f1c599ecc93367c95a30fb8bf34 127.0.0.1:6380 master - 0 1496129142682 3 connected
6fb7dfdb6188a9fe53c48ea32d541724f36434e9 127.0.0.1:6383 master - 0 1496129145699 4 connected
66478bda726ae6ba4e8fb55034d8e5e5804223ff 127.0.0.1:6381 master - 0 1496129147704 2 connected12345678910111213141516

當前已經使這六個節點組成集羣,但是現在還無法工作,因爲集羣節點還沒有分配槽(slot)。


2.3 分配槽

可以看一下6379端口的槽個數

127.0.0.1:6379> CLUSTER INFO
cluster_state:fail
cluster_slots_assigned:0            // 被分配槽的個數爲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_sent:479
cluster_stats_messages_received:479123456789101112

接下來爲節點分配槽空間。通過cluster addslots命令。

redis-cli -h 127.0.0.1 -p 6379 cluster addslots {0..5461}
OK
redis-cli -h 127.0.0.1 -p 6380 cluster addslots {5462..10922}
OK
redis-cli -h 127.0.0.1 -p 6381 cluster addslots {10923..16383}
OK123456

我們將16383個槽平均分配給637963806381端口的節點。再次執行CLUSTER INFO查看一下集羣的狀態:

127.0.0.1:6379> CLUSTER INFO
cluster_state:ok                // 集羣狀態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_sent:1212
cluster_stats_messages_received:1212123456789101112

可以通過CLUSTER NODES來查看分配情況:

127.0.0.1:6379> CLUSTER NODES
e0c7961a1b07ab655bc31d8dfd583da565ec167d 127.0.0.1:6384 master - 0 1496129666347 0 connected
961097d6be64ebd2fd739ff719e97565a8cee7b5 127.0.0.1:6382 master - 0 1496129664844 5 connected
29978c0169ecc0a9054de7f4142155c1ab70258b 127.0.0.1:6379 myself,master - 0 0 1 connected 0-5461
8f285670923d4f1c599ecc93367c95a30fb8bf34 127.0.0.1:6380 master - 0 1496129665846 3 connected 5462-10922
6fb7dfdb6188a9fe53c48ea32d541724f36434e9 127.0.0.1:6383 master - 0 1496129661838 4 connected
66478bda726ae6ba4e8fb55034d8e5e5804223ff 127.0.0.1:6381 master - 0 1496129666848 2 connected 10923-163831234567

目前還有三個節點沒有使用,作爲一個完整的集羣,每個負責處理槽的節點應該具有從節點,保證當主節點出現故障時,可以自動進行故障轉移。集羣模式下,首次啓動的節點和被分配槽的節點都是主節點,從節點負責複製主節點槽的信息和相關數據。使用cluster replicate <nodeid>在從節點上執行。

redis-cli -h 127.0.0.1 -p 6382 cluster replicate 29978c0169ecc0a9054de7f4142155c1ab70258b
OK
redis-cli -h 127.0.0.1 -p 6383 cluster replicate 8f285670923d4f1c599ecc93367c95a30fb8bf34
OK
redis-cli -h 127.0.0.1 -p 6384 cluster replicate 66478bda726ae6ba4e8fb55034d8e5e5804223ff
OK123456

通過CLUSTER NODES可以查看集羣節點的狀態

127.0.0.1:6379> CLUSTER NODES
e0c7961a1b07ab655bc31d8dfd583da565ec167d 127.0.0.1:6384 slave 66478bda726ae6ba4e8fb55034d8e5e5804223ff 0 1496130082754 2 connected
961097d6be64ebd2fd739ff719e97565a8cee7b5 127.0.0.1:6382 slave 29978c0169ecc0a9054de7f4142155c1ab70258b 0 1496130080749 5 connected
29978c0169ecc0a9054de7f4142155c1ab70258b 127.0.0.1:6379 myself,master - 0 0 1 connected 0-5461
8f285670923d4f1c599ecc93367c95a30fb8bf34 127.0.0.1:6380 master - 0 1496130078744 3 connected 5462-10922
6fb7dfdb6188a9fe53c48ea32d541724f36434e9 127.0.0.1:6383 slave 8f285670923d4f1c599ecc93367c95a30fb8bf34 0 1496130079747 4 connected
66478bda726ae6ba4e8fb55034d8e5e5804223ff 127.0.0.1:6381 master - 0 1496130081751 2 connected 10923-16383
12345678

這樣就完成了一個3主3從的Redis集羣搭建。如下圖所示:

這裏寫圖片描述


3. 新增節點

3.1 新增主節點

新增主節點實際上就是添加一個主機後再遷移槽和數據,遷移工作可以用Ruby工具redis-trib.rb完成。

(1)新增一個節點7007作爲主節點修改配置文件:

[root@localhost redis-cluster]# cp -r  redis01 redis07  
[root@localhost redis-cluster]# cd redis07/  
[root@localhost redis07]# sed -i "s/7001/7007/g" ./redis.conf 

(2)啓動7007redis服務:

[root@localhost redis07]# ./redis-server redis.conf   
[root@localhost redis07]# netstat -anp | grep 7007  
tcp        0      0 127.0.0.1:17007         0.0.0.0:*               LISTEN      13441/./redis-serve   
tcp        0      0 127.0.0.1:7007          0.0.0.0:*               LISTEN      13441/./redis-serve

(3)添加使用redis-trib.rb的add-node命令:

[root@localhost redis-cluster]# ./redis-trib.rb add-node 127.0.0.1:7007 127.0.0.1:7002  
>>> Adding node 127.0.0.1:7007 to cluster 127.0.0.1:7002  
>>> Performing Cluster Check (using node 127.0.0.1:7002)  
S: 1f07d76585bfab35f91ec711ac53ab4bc00f2d3a 127.0.0.1:7002  
   slots: (0 slots) slave  
   replicates a5db243087d8bd423b9285fa8513eddee9bb59a6  
M: f9886c71e98a53270f7fda961e1c5f730382d48f 127.0.0.1:7003  
   slots:10923-16383 (5461 slots) master  
   1 additional replica(s)  
M: a5db243087d8bd423b9285fa8513eddee9bb59a6 127.0.0.1:7005  
   slots:5461-10922 (5462 slots) master  
   1 additional replica(s)  
S: 50ce1ea59106b4c2c6bc502593a6a7a7dabf5041 127.0.0.1:7004  
   slots: (0 slots) slave  
   replicates dd19221c404fb2fc4da37229de56bab755c76f2b  
S: 8bb3ede48319b46d0015440a91ab277da9353c8b 127.0.0.1:7006  
   slots: (0 slots) slave  
   replicates f9886c71e98a53270f7fda961e1c5f730382d48f  
M: dd19221c404fb2fc4da37229de56bab755c76f2b 127.0.0.1:7001  
   slots:0-5460 (5461 slots) master  
   1 additional replica(s)  
[OK] All nodes agree about slots configuration.  
>>> Check for open slots...  
>>> Check slots coverage...  
[OK] All 16384 slots covered.  
>>> Send CLUSTER MEET to node 127.0.0.1:7007 to make it join the cluster.  
[OK] New node added correctly.  
[root@localhost redis-cluster]#   

add-node是加入集羣節點,127.0.0.1:7007爲要加入的節點,127.0.0.1:7002 表示加入的集羣的一個節點,用來辨識是哪個集羣,理論上那個集羣的節點都可以

(4)check一下新節點的狀態:

[root@localhost redis-cluster]# ./redis-trib.rb check 127.0.0.1:7007  
>>> Performing Cluster Check (using node 127.0.0.1:7007)  
M: ee3efb90e5ac0725f15238a64fc60a18a71205d7 127.0.0.1:7007  
   slots: (0 slots) master  
   0 additional replica(s) 
……

上面信息可以看到有4個M節點,3個S節點,7007成爲了M主節點,它沒有附屬的從節點,而且Cluster並未給7007分配哈希卡槽(0 slots)

(5)reshard

redis-cluster在新增節點時並未分配卡槽,需要我們手動對集羣進行重新分片遷移數據,需要重新分片命令 reshard:

[root@localhost redis-cluster]# ./redis-trib.rb reshard 127.0.0.1:7005 
……
[OK] All nodes agree about slots configuration.  
>>> Check for open slots...  
>>> Check slots coverage...  
[OK] All 16384 slots covered.  

# 它提示我們需要遷移多少slot到7007上,我們平分16384個哈希槽給4個節點:16384/4 = 4096,我們需要移動4096個槽點到7007上。
How many slots do you want to move (from 1 to 16384)? 
4096

# 需要輸入7007的節點id,ee3efb90e5ac0725f15238a64fc60a18a71205d7
What is the receiving node ID?   
ee3efb90e5ac0725f15238a64fc60a18a71205d7

# redis-trib 會向你詢問重新分片的源節點(source node),即,要從特點的哪個節點中取出 4096 個哈希槽,還是從全部節點提取4096個哈希槽, 並將這些槽移動到7007節點上面。

# 如果我們不打算從特定的節點上取出指定數量的哈希槽,那麼可以向redis-trib輸入 all,這樣的話, 集羣中的所有主節點都會成爲源節點,redis-trib從各個源節點中各取出一部分哈希槽,湊夠4096個,然後移動到7007節點上
Please enter all the source node IDs.  
  Type 'all' to use all the nodes as source nodes for the hash slots.  
  Type 'done' once you entered all the source nodes IDs.  
Source node #1:  
all 

然後開始從別的主節點遷移哈希槽,並且確認。確認之後,redis-trib就開始執行分片操作,將哈希槽一個一個從源主節點移動到7007目標主節點。重新分片結束後我們可以check以下節點的分配情況:

[root@localhost redis-cluster]# ./redis-trib.rb check 127.0.0.1:7001  
>>> Performing Cluster Check (using node 127.0.0.1:7001)  
M: dd19221c404fb2fc4da37229de56bab755c76f2b 127.0.0.1:7001  
   slots:1365-5460 (4096 slots) master  
   1 additional replica(s)  
M: ee3efb90e5ac0725f15238a64fc60a18a71205d7 127.0.0.1:7007  
   slots:0-1364,5461-6826,10923-12287 (4096 slots) master  
   0 additional replica(s) 

   ……

slots:0-1364,5461-6826,10923-12287 (4096 slots) master: 可以看到7007節點分片的哈希槽片不是連續的,間隔的移動。


3.2 新增從節點

(1)新增一個節點7008節點,使用add-node —slave命令:

[root@localhost redis-cluster]# cp -r redis01/ redis08  
[root@localhost redis-cluster]# cd redis08/  
[root@localhost redis08]# sed -i "s/7001/7008/g" ./redis.conf  
[root@localhost redis08]# ./redis-server redis.conf   
……
[root@localhost redis-cluster]# ./redis-trib.rb add-node --slave 127.0.0.1:7008 127.0.0.1:7001>>> Adding node 127.0.0.1:7008 to cluster 127.0.0.1:7001

nodeid爲要加到master主節點的node id,127.0.0.1:7008爲新增的從節點,127.0.0.1:7000爲集羣的一個節點(集羣的任意節點都行),用來辨識是哪個集羣;如果沒有給定那個主節點--master-id的話,redis-trib將會將新增的從節點隨機到從節點較少的主節點上


4. 移除節點

4.1 移除主節點

移除節點使用redis-trib的del-node命令:

[root@localhost redis-cluster]# ./redis-trib.rb del-node 127.0.0.1:7002 dd19221c404fb2fc4da37229de56bab755c76f2b
>>> Removing node dd19221c404fb2fc4da37229de56bab755c76f2b from cluster 127.0.0.1:7002  
[ERR] Node 127.0.0.1:7001 is not empty! Reshard data away and try again.  
[root@localhost redis-cluster]#   

127.0.0.1:7002爲集羣節點,dd19221c404fb2fc4da37229de56bab755c76f2b爲要刪除的主節點的ID。redis cluster提示7001已經有數據了,不能夠被刪除,需要將他的數據轉移出去,也就是和新增主節點一樣需重新分片。

(1)利用reshard將7001上的槽移到其他節點

[root@localhost redis-cluster]# ./redis-trib.rb reshard 127.0.0.1:7002  

>> Check for open slots...  
>>> Check slots coverage...  
[OK] All 16384 slots covered.  
How many slots do you want to move (from 1 to 16384)?   
4096

What is the receiving node ID?
all

Please enter all the source node IDs.  
  Type 'all' to use all the nodes as source nodes for the hash slots.  
  Type 'done' once you entered all the source nodes IDs.  
Source node #1:  dd19221c404fb2fc4da37229de56bab755c76f2b
Source node #2:done  
Do you want to proceed with the proposed reshard plan (yes/no)? yes  

(2)刪除7001

[root@localhost redis-cluster]# ./redis-trib.rb del-node 127.0.0.1:7002 dd19221c404fb2fc4da37229de56bab755c76f2b  
>>> Removing node dd19221c404fb2fc4da37229de56bab755c76f2b from cluster 127.0.0.1:7002  
>>> Sending CLUSTER FORGET messages to the cluster...  
>>> SHUTDOWN the node.  
[root@localhost redis-cluster]#   

4.2 移除從節點

刪除從節點比較方便,直接使用 ./redis-trib.rb del-node 就夠了。


5. 請求路由

5.1 請求重定向

在集羣模式下,Redis接收任何鍵相關命令時首先計算鍵對應的槽,再根據槽找出所對應的節點,如果節點是自身,則處理鍵命令;否則回覆MOVED重定向錯誤,通知客戶端請求正確的節點。這個過程稱爲MOVED重定向。

這裏寫圖片描述


5.2 Smart客戶端

這裏寫圖片描述


6. 故障轉移

6.1 故障發現流程

(1)當一個節點在超時時間內一直PING不通節點F,它會主觀下線這個節點,也就是將這個節點F標記爲P-Fail,並且會在集羣內傳播。

(2)因爲ping/pong消息的消息體會攜帶集羣1/10的其他節點狀態數據,通過Gossip消息傳播,集羣內節點不斷收集到故障節點的下線報告。當半數以上持有槽的主節點都標記某個節點是主觀下線時,觸發客觀下線流程。

(3)主節點向集羣廣播一條fail消息,通知所有的節點將故障節點標記爲客觀下線。這會觸發故障節點的從節點的故障轉移流程

這裏寫圖片描述

6.2 主節點故障的恢復

故障節點變爲客觀下線後,如果下線節點是持有槽的主節點則需要在它的從節點中選出一個替換它,從而保證集羣的高可用。下線主節點的所有從節點承擔故障恢復的義務,當從節點通過內部定時任務發現自身複製的主節點進入客觀下線時,將會觸發故障恢復流程。

選舉

(1)原則是,複製偏移量越大說明從節點延遲越低,那麼它應該具有更高的優先級來替換故障主節點,從而可以優先發起選舉。

(2)從節點廣播選舉消息,每個持有槽Slot的主節點只有一張選票,一旦從節點收到某個節點的回覆,就獲得了一張選票,票數超過半數主節點的選票之後就可以開始替換主節點了。

之所以不讓從節點自己選舉是因爲一個主節點的從節點的個數是不確定的,而選舉需要節點個數大於3並且是奇數。

(3)替換主節點。向集羣所有節點廣播自己現在是主節點了。

發佈了94 篇原創文章 · 獲贊 43 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章