天天看點

Redis詳解(十)------ 叢集模式詳解

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的結果分别存儲到三台伺服器上,如下所示:

   

Redis詳解(十)------ 叢集模式詳解

  特點:鍵值業務相關;資料分散,但是容易造成通路傾斜;支援順序通路;支援批量操作

  ②、哈希分布

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

  

Redis詳解(十)------ 叢集模式詳解

  特點:資料分散度高;鍵值分布與業務無關;不支援順序通路;支援批量操作。

3、一緻性哈希分布

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

  ①、增加節點

Redis詳解(十)------ 叢集模式詳解

   如上圖所示,總共10個資料通過節點取餘hash(key)%/3 的方式分布到3個節點,這時候由于通路量變大,要進行擴容,由 3 個節點變為 4 個節點。

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

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

  ②、删除節點

Redis詳解(十)------ 叢集模式詳解

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

  那麼如何使得叢集中新增節點或者删除節點時,資料遷移量最少?——一緻性雜湊演算法誕生。

  PS:關于一緻性雜湊演算法,我會另外寫一篇部落格進行詳細介紹,這裡隻是大概介紹一下。

Redis詳解(十)------ 叢集模式詳解

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

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

Redis詳解(十)------ 叢集模式詳解

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

Redis詳解(十)------ 叢集模式詳解

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

4、Redis Cluster虛拟槽分區

  Redis叢集資料分布沒有使用一緻性哈希分布,而是使用虛拟槽分區概念。

  Redis内部内置了序号 0-16383 個槽位,每個槽位可以用來存儲一個資料集合,将這些槽位按順序配置設定到叢集中的各個節點。每次新的資料到來,會通過哈希函數 CRC16(key) 算出将要存儲的槽位下标,然後通過該下标找到前面配置設定的Redis節點,最後将資料存儲到該節點中。

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

Redis詳解(十)------ 叢集模式詳解

   至于為什麼Redis不使用一緻性哈希分布,而是虛拟槽分區。因為虛拟槽分區雖然沒有一緻性哈希那麼靈活,但是CRC16(key)%16384 已經分布很均勻了,并且對于後面節點增删操作起來也很友善。

5、原生搭建 Redis Cluster

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

①、伺服器清單

Redis詳解(十)------ 叢集模式詳解

②、配置各個節點參數

#配置端口
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 的字樣:

Redis詳解(十)------ 叢集模式詳解

③、建立各個節點通信

   這裡有 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 6382redis-cli -p 6379 -a 123 cluster meet 192.168.14.102 6380redis-cli -p 6379 -a 123 cluster meet 192.168.14.102 6383redis-cli -p 6379 -a 123 cluster meet 192.168.14.103 6381redis-cli -p 6379 -a 123 cluster meet 192.168.14.103 6384           

  執行完畢後,可以檢視節點通信資訊:

redis-cli -p 6379 -a 123 cluster nodes           

  結果如下:

Redis詳解(十)------ 叢集模式詳解

   或者執行如下指令:

redis-cli -p 6379 -a 123 cluster info           
Redis詳解(十)------ 叢集模式詳解

④、配置設定槽位

  由于我們是三主三從的架構,是以隻需要對主伺服器配置設定槽位即可。三個節點,配置設定序号為 0-16383 ,總共16384 個槽位。

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

  配置設定槽位的指令如下:

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

  比如,對于Node1主節點,我們執行指令如下:

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

  另外兩個節點對于上面的指令更改一下槽位數,然後檢視叢集資訊:  

Redis詳解(十)------ 叢集模式詳解

  檢視Node1節點資訊:

Redis詳解(十)------ 叢集模式詳解

⑤、主從配置

  指令如下:

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

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

Redis詳解(十)------ 叢集模式詳解

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

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

  執行三條指令完畢後,檢視節點資訊:

Redis詳解(十)------ 叢集模式詳解

   這時候,叢集狀态是成功了。

⑥、測試

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

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

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

Redis詳解(十)------ 叢集模式詳解

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

Redis詳解(十)------ 叢集模式詳解

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 檢視版本資訊。

Redis詳解(十)------ 叢集模式詳解

   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           

  添加完畢後,這時候檢視叢集狀态

Redis詳解(十)------ 叢集模式詳解

   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詳解(十)------ 叢集模式詳解

   我們發現已經給該節點配置設定了槽位。

④、将新增的從節點添加到叢集中

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從。

Redis詳解(十)------ 叢集模式詳解

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

Redis詳解(十)------ 叢集模式詳解

   自此,大功告成。

8、叢集收縮

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

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

  移除之前的節點資訊:

Redis詳解(十)------ 叢集模式詳解
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           

  移除完後,檢視節點資訊,發現6390已經沒有槽位了。

Redis詳解(十)------ 叢集模式詳解

 ②、移除待删除主從節點

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

  是以,我們應該先移除 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           
redis-cli -p 6379 -a 123 --cluster del-node 192.168.14.101:6379 4a0779c7baef78c8fd0f2bb6e73f29375e00133d