在上一篇博客我們介紹了------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
移除完后,查看節點信息,發現6390已經沒有槽位了。
②、移除待刪除主從節點
注意:要首先移除從節點,然后再移除主節點,因為如果你先移除主節點,會觸發集群的故障轉移。
所以,我們應該先移除 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