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
&n