Redis詳解(十)—— 從零開始搭建集群

  在上一篇部落格我們介紹了——Redis哨兵(Sentinel)模式,哨兵模式主要是解決高可用問題,在master節點宕機時,slave節點能夠自動切換成為master節點

  本篇部落格我們來介紹Redis的另外一種模式——集群模式.

  PS:我這裡搭建演示的版本是redis-5.0.5,這個版本對於集群搭建會有很大的簡化,比如最常用的redis-trib.rb腳本功能已經集成到redis-cli工具中了,具體下面會詳細介紹。

1、為什麼需要集群?

  ①、並發量

  通常來說,單台Redis能夠執行10萬/秒的命令,這個並發基本上能夠滿足我們所有需求了,但有時候比如做離線計算,為了更快的得出結果,有時候我們希望超過這個並發,那這個時候單機就不滿足我們需求了,就需要集群了.

  ②、數據量

  通常來說,單台伺服器的記憶體大概在16G-256G之間,前面我們說Redis數據量都是存在記憶體中的,那如果實際業務要保存在Redis的數據量超過了單台機器的記憶體,這個時候最簡單的方法是增加伺服器記憶體,但是單台伺服器記憶體不可能無限制的增加,縱向擴展不了了,便想到如何進行橫向擴展.這時候我們就會想將這些業務數據分散存儲在多台Redis伺服器中,但是要保證多台Redis伺服器能夠無障礙的進行記憶體數據溝通,這也就是Redis集群.

2、數據分區方式

  對於集群來說,如何將原來單台機器上的數據拆分,然後盡量均勻的分布到多台機器上,這是我們創建集群首先要考慮的一個問題,通常來說,有如下兩種數據分區方式.

  ①、順序分布

  比如我們有100W條數據,有3台伺服器,我們可以將100W/3的結果分別存儲到三台伺服器上,如下所示:

   

  特點:鍵值業務相關;數據分散,但是容易造成訪問傾斜;支援順序訪問;支援批量操作

  ②、哈希分布

  同樣是100W條數據,有3台伺服器,通過自定義一個哈希函數,比如節點取余的方法,餘數為0的存在第一台伺服器,餘數為1的存在第二台伺服器,餘數為2的存儲在第三台伺服器.如下所示:

  

  特點:數據分散度高;鍵值分布與業務無關;不支援順序訪問;支援批量操作。

3、一致性哈希分布

  問題:對於上面介紹的哈希分布,大家可以想一下,如果向集群中增加節點,或者集群中有節點宕機,這個時候應該怎麼處理?

  ①、增加節點

  

 

   如上圖所示,總共10個數據通過節點取余hash(key)%/3 的方式分布到3個節點,這時候由於訪問量變大,要進行擴容,由 3 個節點變為 4 個節點。

  我們發現,如圖所示,數據除了標紅的1 2 沒有進行遷移,別的數據都要進行變動,達到了80%,如果這時候並發很高,80%的數據都要從下層節點(比如資料庫)獲取,會給下層節點造成很大的訪問壓力,這是不能接受的。

  即使我們進行翻倍擴容,從3個節點增加到6個節點,其數據遷移也在50%左右。

  ②、刪除節點

  

  上圖其實不管是哪一個節點宕機,其數據遷移量都會超過50%。基本上也是我們所不能接受的。

  那麼如何使得集群中新增節點或者刪除節點時,數據遷移量最少?——一致性哈希演算法誕生。

  PS:關於一致性哈希演算法,我會另外寫一篇部落格進行詳細介紹,這裡只是大概介紹一下。

  

 

   假設有一個哈希環,從0到2的32次方,均勻的分成三份,中間存放三個節點,沿著順時針旋轉,從Node1到Node2之間的數據,存放在Node2節點上;從Node2到Node3之間的數據,存放在Node3節點上,依次類推。

  假設Node1節點宕機,那麼原來Node3到Node1之間的數據這時候改為存放到Node2節點上,Node2到Node3之間數據保持不變,原來Node1到Node2之間的數據還是存放在Node2上,也就是隻影響三分之一的數據,節點越多,影響數據越少。

  

  同理,假設增加一個節點,影響的數據甚至更少。

  

 

   當然,實際業務中並不是你節點均勻分布,訪問就會很平均,這時候容易造成訪問傾斜的問題,這裡就會引出虛擬節點的定義。我這裡就不做詳解了。

4、Redis Cluster虛擬槽分區

  Redis集群數據分布沒有使用一致性哈希分布,而是使用虛擬槽分區概念。

  Redis內部內置了序號 0-16383 個槽位,每個槽位可以用來存儲一個數據集合,將這些槽位按順序分配到集群中的各個節點。每次新的數據到來,會通過哈希函數 CRC16(key) 算出將要存儲的槽位下標,然後通過該下標找到前面分配的Redis節點,最後將數據存儲到該節點中。

  具體情況如下圖:(以集群有3個節點為例)

  

   至於為什麼Redis不使用一致性哈希分布,而是虛擬槽分區。因為虛擬槽分區雖然沒有一致性哈希那麼靈活,但是CRC16(key)%16384 已經分布很均勻了,並且對於後面節點增刪操作起來也很方便。

5、原生搭建 Redis Cluster

  集群以三主三從的模式來搭建。

①、伺服器列表

  

 

②、配置各個節點參數

#配置埠
port ${port}
#以守護進程模式啟動
daemonize yes
#pid的存放文件
pidfile /var/run/redis_${port}.pid
#日誌文件名
logfile "redis_${port}.log"
#存放備份文件以及日誌等文件的目錄
dir "/opt/redis/data"  
#rdb備份文件名
dbfilename "dump_${port}.rdb"
#開啟集群功能
cluster-enabled yes
#集群配置文件,節點自動維護
cluster-config-file nodes-${port}.conf
#集群能夠運行不需要集群中所有節點都是成功的
cluster-require-full-coverage no

  配置完成後,通過 redis-server redis.conf 命令啟動這六個節點。

  啟動之後,進程後面會有 cluster 的字樣:

  

③、建立各個節點通訊

   這裡有 6 個節點,我們只需要拉通 1 個節點和另外 5 個節點之間通訊,那麼每兩個節點就能夠通訊了。命令如下:

redis-cli -h -p ${port1} -a ${password} cluster meet ${ip2}  ${port2} 

  這裡的 -a 參數表示該Redis節點有密碼,如果沒有可以不用加此參數。

  實例中的 6 個節點,分別進行如下命令:

redis-cli -p 6379 -a 123 cluster meet 192.168.14.101 6382
redis-cli -p 6379 -a 123 cluster meet 192.168.14.102 6380
redis-cli -p 6379 -a 123 cluster meet 192.168.14.102 6383
redis-cli -p 6379 -a 123 cluster meet 192.168.14.103 6381
redis-cli -p 6379 -a 123 cluster meet 192.168.14.103 6384

  執行完畢後,可以查看節點通訊資訊:

redis-cli -p 6379 -a 123 cluster nodes

  結果如下:

  

 

   或者執行如下命令:

redis-cli -p 6379 -a 123 cluster info

  結果如下:

  

④、分配槽位

  由於我們是三主三從的架構,所以只需要對主伺服器分配槽位即可。三個節點,分配序號為 0-16383 ,總共16384 個槽位。

Node1:0~5460
Node2:5461~10922
Node3:10923~16383

  分配槽位的命令如下:

redis-cli -p ${port} -a ${password} cluster addslots {${startSlot}..${endSlot}}

  比如,對於Node1主節點,我們執行命令如下:

redis-cli -p 6379 -a 123 cluster addslots {0..5462}

  另外兩個節點對於上面的命令更改一下槽位數,然後查看集群資訊:  

  查看Node1節點資訊:

  

⑤、主從配置

  命令如下:

redis-cli -p ${port} -a {password} cluster replicate ${nodeId}

  前面的${port} 表示從節點的埠,這裡的nodeId表示主節點的nodeId,如下:

  

 

   如果弄反了,會報如下錯誤:

(error) ERR To set a master the node must be empty and without assigned slots.

  執行三條命令完畢後,查看節點資訊:

  

   這時候,集群狀態是成功了。

⑥、測試

  經過如上幾步操作,集群搭建成功,我們通過如下命令進入客戶端:

redis-cli -c -p ${port} -a {password}

  注意:必須要加 -c 參數,否則進行鍵值對操作時會報如下錯誤:

  

   正確進入後,可以正確存值和取值。

  

6、腳本搭建Redis Cluster

   上面原生命令安裝Redis Cluster 走下來其實挺費勁的,在實際生產環境中,如果集群數量比較大,操作還是容易出錯的。

  不過Redis官方提供了一個安裝集群的腳本,在Redis安裝目錄的src目錄下——redis-trib.rb,使用該腳本可以快速搭建Redis Cluster集群。

  注意:redis版本在5之前的集群運行該腳本需要安裝ruby環境,而redis5.0之後已經將redis-trib.rb 腳本的功能全部集成到redis-cli之中了,所以如果當前版本是Redis5,那麼可以不用安裝ruby環境。

  下面我分別介紹這兩種方法。

①、Redis5之前使用redis-trib.rb腳本搭建

  redis-trib.rb腳本使用ruby語言編寫,所以想要運行次腳本,我們必須安裝Ruby環境。安裝命令如下:

yum -y install centos-release-scl-rh
yum -y install rh-ruby23  
scl enable rh-ruby23 bash
gem install redis

  安裝完成後,我們可以使用 ruby -v 查看版本資訊。

  

   Ruby環境安裝完成後。運行如下命令:

redis-trib.rb create --replicas 1 192.168.14.101:6379 192.168.14.102:6380 192.168.14.103:6381 192.168.14.101:6382 192.168.14.102:6383 192.168.14.103:6384

  關於這個命令的解釋下面會一起介紹。

②、Redis5版本集群搭建 

  前面我們就說過,redis5.0之後已經將redis-trib.rb 腳本的功能全部集成到redis-cli中了,所以我們直接使用如下命令即可:

redis-cli -a ${password} --cluster create 192.168.14.101:6379 192.168.14.102:6380 192.168.14.103:6381 192.168.14.101:6382 192.168.14.102:6383 192.168.14.103:6384 --cluster-replicas 1

  ①、${password} 表示連接Redis的密碼,通常整個集群我們要麼不設置密碼,要麼設置成一樣的。

  ②、後面的六個ip:port,按照順序,前面三個是主節點,後面三個是從節點,順序不能錯。

  ③、最後數字 1 表示一個主節點只有一個從節點。和前面的配置相對應。

7、集群擴容

  這裡新增兩個埠分別是 6390、6391的節點。其中6391節點是6390節點的從節點。

①、配置新增節點文件

  比如,我們將6379節點的配置文件redis.conf 拷貝兩份,然後將裡面的配置文件裡面的字元串 6379 分別替換成 6390 和 6391。

  :%s/6379/6390/g,:%s/6379/6391/g

  替換完成之後,分別啟動這兩個節點。

  這時候這兩個節點都不在集群當中,是兩個孤兒節點。

②、將新增主節點加入到集群中

  命令如下:

redis-cli -p existing_port -a ${password} --cluster add-node new_host:new_port existing_host:existing_port

  我這裡是將新增的主節點 6390 添加到原來的集群中。

redis-cli -p 6379 -a 123 --cluster add-node 192.168.14.101:6390 192.168.14.101:6379

  添加完畢後,這時候查看集群狀態

 

   6390節點已經存在集群中了,但是還沒有分配槽位。

③、為新增主節點分配槽位

  分配命令如下:

redis-cli -p existing_port -a ${password} --cluster reshard existing_host:existing_port

  後面的existing_host:existing_port表示原來集群中的任意一個節點,這個命令表示將源節點的一部分槽位分配個新增的節點。

  在分配過程中,會出現如下幾個提示:

#後面的2000表示分配2000個槽位給新增節點
How many slots do you want to move (from 1 to 16384)? 2000
#表示接受節點的NodeId,填新增節點6390的
What is the receiving node ID? 64a0779c7baef78c8fd0f2bb6e73f29375e00133d
#這裡填槽的來源,要麼填all,表示所有master節點都拿出一部分槽位分配給新增節點;
#要麼填某個原有NodeId,表示這個節點拿出一部分槽位給新增節點
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-cli -p 6379 -a 123 --cluster add-node 192.168.14.101:6391 192.168.14.101:6379

⑤、建立新增節點的主從關係

  命令如下:

redis-cli -p ${port} -a {password} cluster replicate ${nodeId}

  前面的${port} 表示從節點的埠,這裡的nodeId表示主節點的nodeId。

⑥、測試

  查看節點資訊,發現4主4從。

   在6379節點新增一個字元串 (k4,v4),然後到6390節點查看:

  

   自此,大功告成。

8、集群收縮

  這裡我們將上一步添加的主從節點6390和6391從集群中移除。

①、遷移待移除節點的槽位

  移除之前的節點資訊:

redis-cli -p existing_port -a {Redis登錄密碼} --cluster reshard --cluster-from {待移除的NodeId} --cluster-to {接受移除節點的NodeId} --cluster-slots {移除的槽位個數} existing_host:existing_port

  比如,我這裡要移除主節點 6390 的所有槽位,給6379節點。

redis-cli -p 6379 -a 123 --cluster reshard --cluster-from 4a0779c7baef78c8fd0f2bb6e73f29375e00133d --cluster-to 001a22b1edae6ea1699b753d193871824723f375 --cluster-slots 2000 192.168.14.101:6379

  移除完後,查看節點資訊,發現6379已經沒有槽位了。

 

 ②、移除待刪除主從節點

  注意:要首先移除從節點,然後再移除主節點,因為如果你先移除主節點,會觸發集群的故障轉移。

  所以,我們應該先移除 6391 從節點,然後在移除 6390 主節點。移除命令如下:

redis-cli -p existing_port -a {Redis登錄密碼} --cluster del-node host:port {待刪除的NodeId}

  刪除 6391 從節點:

redis-cli -p 6379 -a 123 --cluster del-node 192.168.14.101:6379 3622ec34956b624358722e6f4a2b762574d35bf0

  刪除 6390 主節點:

redis-cli -p 6379 -a 123 --cluster del-node 192.168.14.101:6379 4a0779c7baef78c8fd0f2bb6e73f29375e00133d