redis集羣

redis集羣

redis集羣:http://www.redis.cn/topics/cluster-tutorial.html

redis cluster

   在哨兵sentinel機制中,可以解決redis高可用的問題,即當master故障後可以自動將slave提升爲master從而可以保證redis服務的正常使用,但是哨兵機制無法解決redis單機寫入的瓶頸問題,即單機的redis寫入性能受限於單機的內存大小、併發數量、網卡速率等因素,因此redis官方在redis 3.0版本之後推出了無中心架構的redis cluster機制,在無中心的redis集羣當中,其每個節點保存當前節點數據和整個集羣狀態,每個節點都和其他所有節點連接,特點如下:

  • 所有Redis節點使用(PING機制)互聯
  • ②集羣中某個節點的失效,是整個集羣中超過半數的節點監測都失效纔算真正的失效
  • ③客戶端不需要proxy即可直接連接redis,應用程序需要寫全部的redis服務器IP
  • ④redis cluster把所有的redis node映射到0-16383個槽位(slot)上,讀寫需要到指定的redis node上進行操作,因此有多少個reids node相當於redis併發擴展了多少倍
  • ⑤Redis cluster預先分配16384個(slot)槽位,當需要在redis集羣中寫入一個key-value的時候,會使用CRC16(key) mod 16384之後的值,決定將key寫入值哪一個槽位從而決定寫入哪一個Redis節點上,從而有效解決單機瓶頸

redis cluster的實現方式

   redis集羣與高可用有多重方式可以實現,比如高可用可以使用哨兵或者redis主從+keepalived的方式實現,redis集羣支持多重方式,比如客戶端分片、代理方式、redis cluster以及Coodis,每個實現方式都有自己的優缺點,具體方案及實現如下:

客戶端分片:

   mysql、memcached以及redis等都可以通過客戶端分片實現,其中mysql還可以通過客戶端實現分庫分表,客戶端分片是在客戶端將key進行hash按照不同的值保存到不同的redis 服務器,讀取的話也是按照不同的位置進行讀取,優勢是比較靈活,不存在單點故障,缺點是添加節點需要重新配置分片算法,並且需要手動同步數據,在緩存場景客戶端分片最適用於使用memcached,因爲緩存是可以丟失一部分數據的,但是memcached可以做集羣進行數據同步。

Redis Cluster:

   在3.0版本以後支持,無中心,在某種情況下會造成數據丟失,其也是通過算法將數據分片保存至某個redis服務器,即不再通過客戶端計算key保存的redis服務器,redis服務器需要提前設置好自己所負責的槽位,比如redis A負責處理0-5000的哈希槽位數據,redis B負責處理5001-10000的hash槽位數據,redis C負責處理10001-16384的hash槽位數據,redis cluster需要特定的客戶端,要求客戶端必須支持集羣協議 ,但是目前還沒有比較好的客戶端。

   這種將哈希槽分佈到不同節點的做法使得用戶可以很容易地向集羣中添加或者刪除節點。 比如說:如果用戶將新節點D添加到集羣中,那麼集羣只需要將節點A、B、 C中的某些槽移動到節點D就可以了。

代理:

   例如Twemproxy,由proxy代替客戶端換和服務端實現分片,可以使用在緩存場景中允許數據丟失的場景,其還支持memcached,可以爲proxy配置算法,缺點爲twemproxy是瓶頸,不支持數據遷移,官方github地址https://github.com/twitter/twemproxy/

Codis:

Codis:豌豆莢的開源方案,目前redis集羣比較穩定的方案,豌豆莢gitlab地址https://github.com/pingcap:

  • 豌豆莢codis項目官方github地址https://github.com/CodisLabs/codis

  • 可以無縫遷移到codis

  • 可以動態擴容和縮容

  • 多業務完全透明,業務不知道運行的是codis

  • 支持多核心CPU,twemproxy只能單核

  • codis是有中心基於proxy的設計,是客戶端像連接單機一樣操作proxy

  • 有部分命令不能支持,比如keys *等

  • 支持group劃分,組內可以設置一個主多個從,通過sentinel 監控redis主從,當主down了自動將從切換爲主

  • 設置的進程要最大等於CPU的核心,不能超過CPU的核心數

  • 其基於zookeeper,裏面保存的是key保存的redis主機位置,因此zookeeper要做高可用

  • 監控可以使用接口和dashboard

  • tidb豌豆莢團隊實現的分佈式mysql數據庫,github地址https://github.com/pingcap/tidb

Redis Cluster介紹

Redis集羣是一個提供在多個Redis間節點間共享數據的程序集。

Redis集羣並不支持處理多個keys的命令,因爲這需要在不同的節點間移動數據,從而達不到像Redis那樣的性能,在高負載的情況下可能會導致不可預料的錯誤.

Redis 集羣通過分區來提供一定程度的可用性,在實際環境中當某個節點宕機或者不可達的情況下繼續處理命令,Redis集羣的優勢:

  • 自動分割數據到不同的節點上。
  • 整個集羣的部分節點失敗或者不可達的情況下能夠繼續處理命令。

Redis Cluster的數據分片

Redis 集羣沒有使用一致性hash, 而是引入了哈希槽的概念.
Redis 集羣有16384個哈希槽,每個key通過CRC16校驗後對16384取模來決定放置哪個槽。集羣的每個節點負責一部分hash槽,舉個例子,比如當前集羣有3個節點,那麼:

  • 節點 A 包含 0 到 5500號哈希槽.
  • 節點 B 包含5501 到 11000 號哈希槽.
  • 節點 C 包含11001 到 16384號哈希槽.

  這種結構很容易添加或者刪除節點。比如如果我想新添加個節點D, 我需要從節點 A, B, C中得部分槽到D上。如果我想移除節點A,需要將A中的槽移到B和C節點上,然後將沒有任何槽的A節點從集羣中移除即可。由於從一個節點將哈希槽移動到另一個節點並不會停止服務,所以無論添加刪除或者改變某個節點的哈希槽的數量都不會造成集羣不可用的狀態。

哈希槽

  redis cluster採用數據分片的哈希槽來進行數據存儲和數據的讀取。redis cluster一共有2^14(16384)個槽,所有的master節點都會有一個槽區比如0~1000,槽數是可以遷移的。master節點的slave節點不分配槽,只擁有讀權限。但是注意在代碼中redis cluster執行讀寫操作的都是master節點,並不是你想的讀是從節點,寫是主節點。第一次新建redis cluster時,16384個槽是被master節點均勻分佈的。

哈希槽和一致性哈希相比:

  • 哈希槽並不是閉合的,key的定位規則是根據CRC-16(key)%16384的值來判斷屬於哪個槽區,從而判斷該key屬於哪個節點,而一致性哈希是根據hash(key)的值來順時針找第一個hash(ip)的節點,從而確定key存儲在哪個節點。

  • 一致性哈希是創建虛擬節點來實現節點宕機後的數據轉移並保證數據的安全性和集羣的可用性的。redis cluster是採用master節點有多個slave節點機制來保證數據的完整性的,master節點寫入數據,slave節點同步數據。當master節點掛機後,slave節點會通過選舉機制選舉出一個節點變成master節點,實現高可用。但是這裏有一點需要考慮,如果master節點存在熱點緩存,某一個時刻某個key的訪問急劇增高,這時該mater節點可能操勞過度而死,隨後從節點選舉爲主節點後,同樣宕機,一次類推,造成緩存雪崩。

Redis cluster基本架構

假如三個主節點分別是:A, B, C 三個節點,採用哈希槽 (hash slot)的方式來分配16384個slot 的話,它們三個節點分別承擔的slot區間是:

  • 集羣自動分配是均分的:
  • 節點A覆蓋 0-5460
  • 節點B覆蓋 5461-10922
  • 節點C覆蓋 10923-16383

Redis cluster的架構雖然解決了併發的問題,但是又引入了一個新的問題,每個Redis master的高可用如何解決?

  • 在每個節點上增加一個slave節點,以實現redis cluster的高可用
部署redis集羣

部署redis cluster集羣的前提:

1.每個redis node節點採用相同的硬件配置、相同的密碼、相同的redis版本。

2.每個節點必須開啓的參數

  • cluster-enabled yes #必須開啓集羣狀態,開啓後redis 進程會有cluster顯示
  • cluster-config-file nodes-6380.conf #此文件有redis cluster集羣自動創建和維護,不需要任何手動操作

3.所有redis服務器必須沒有任何數據

4.先啓動爲單機redis且沒有任何key value

5集羣節點時間同步.

注:集羣的通信端口在原服務的監聽端口上加10000

部署拓撲圖

  • 生產環境建議直接6臺服務器

  • 實驗主機:centos7.6

(1)首先在每臺主機上部署redis
  • 在其中一臺編譯安裝之後,將其打包發送至其他主機,也可以考慮使用ansible。
(2)創建集羣
  • 注:只需在其中一臺主機上安裝集羣管理工具redis-trib.rb即可,然後在這一臺主機管理redis cluster
(2-1)創建集羣:redis版本3和版本4

Redis 3和4版本:

   需要使用到集羣管理工具redis-trib.rb,redis-trib.rb是redis官方推出的管理redis集羣的工具,集成在redis的源碼src目錄下,是基於redis提供的集羣命令封裝成簡單、便捷、實用的操作工具,redis-trib.rb是redis作者用ruby開發完成的,centos 系統yum安裝的ruby存在版本較低問題,如下:

  • redis-trib.rb工具在redis源碼包路徑中,但是redis-trib.rb工具的運行需要依賴於ruby環境和redis模塊(gem install redis安裝)
# cp /usr/local/src/redis-4.0.14/src/redis-trib.rb /usr/local/bin/
  • ①解決ruby環境問題,編譯ruby:
src]# wget https://cache.ruby-lang.org/pub/ruby/2.5/ruby-2.5.5.tar.gz
src]# tar xf ruby-2.5.5.tar.gz
src]# cd ruby-2.5.5
ruby-2.5.5]# yum -y install gcc zlib-devel curl-devel openssl-devel
ruby-2.5.5]# ./configure
ruby-2.5.5]# make -j 2
ruby-2.5.5]# make install
ruby-2.5.5]# gem install redis


注:ruby編譯的安裝環境需要安裝以上包,否則也能安裝ruby成功,但是好像很多功能無法使用
# gem install redis執行此命令時,可能報錯:
    ERROR:  Loading command: install (LoadError)
	    cannot load such file -- zlib
    ERROR:  While executing gem ... (NoMethodError)
        undefined method `invoke_with_build_args' for nil:NilClass
# 然後去網上搜出現此錯誤的解決辦法:進入ruby源碼目錄逐個安裝,特別麻煩
  • ②解決redis模塊(gem install redis安裝)
# gem install redis  #如果無法在線安裝,可以下載reids模塊安裝包離線安裝
  • 測試redis-trib.rb工具可用
[root@centos7-27 ~]# redis-trib.rb  然後會列出此命令的使用幫助則表示此命令可以使用啦
redis-trib.rb help
Usage: redis-trib <command> <options> <arguments ...>

#創建集羣
create          host1:port1 ... hostN:portN  
                  --replicas <arg> #帶上該參數表示是否有從,arg表示從的數量
#檢查集羣
check           host:port
#查看集羣信息
info            host:port
#修復集羣
fix             host:port
                  --timeout <arg>
#在線遷移slot  
reshard         host:port       #個是必傳參數,用來從一個節點獲取整個集羣信息,相當於獲取集羣信息的入口
                  --from <arg>  #需要從哪些源節點上遷移slot,可從多個源節點完成遷移,以逗號隔開,傳遞的是節點的node id,還可以直接傳遞--from all,這樣源節點就是集羣的所有節點,不傳遞該參數的話,則會在遷移過程中提示用戶輸入
                  --to <arg>    #slot需要遷移的目的節點的node id,目的節點只能填寫一個,不傳遞該參數的話,則會在遷移過程中提示用戶輸入。
                  --slots <arg> #需要遷移的slot數量,不傳遞該參數的話,則會在遷移過程中提示用戶輸入。
                  --yes         #設置該參數,可以在打印執行reshard計劃的時候,提示用戶輸入yes確認後再執行reshard
                  --timeout <arg>  #設置migrate命令的超時時間。
                  --pipeline <arg> #定義cluster getkeysinslot命令一次取出的key數量,不傳的話使用默認值爲10。
#平衡集羣節點slot數量  
rebalance       host:port
                  --weight <arg>
                  --auto-weights
                  --use-empty-masters
                  --timeout <arg>
                  --simulate 不會真正遷移,測試用的
                  --pipeline <arg> 一次遷移多少分數據
                  --threshold <arg>
#將新節點加入集羣 
add-node        new_host:new_port existing_host:existing_port
                  --slave
                  --master-id <arg>
#從集羣中刪除節點
del-node        host:port node_id
#設置集羣節點間心跳連接的超時時間
set-timeout     host:port milliseconds
#在集羣全部節點上執行命令
call            host:port command arg arg .. arg
#將外部redis數據導入集羣
import          host:port
                  --from <arg>
                  --copy
                  --replace

   #修改密碼redis登錄密碼

  • 因爲在redis.conf配置文件中設置了requirepass 123456,直接創建集羣將會報錯
[root@centos7-17 src]# vim /usr/local/lib/ruby/gems/2.5.0/gems/redis-4.1.3/lib/redis/client.rb

創建redis cluster
[root@centos7-17 src]# redis-trib.rb create --replicas 1 192.168.38.17:6379 192.168.38.27:6379 192.168.38.37:6379 192.168.38.47:6379 192.168.38.57:6379 192.168.38.67:6379


查看各主機redis的狀態:

> info
# Cluster
cluster_enabled:1    #cluster_enabled 要爲1

==> 至此cluster集羣配置完成

(2-2)創建集羣:redis版本5
  • redis版本5 統一使用redis-cli管理集羣
[root@redis-s1 ~]# redis-cli -a 123456 --cluster create  192.168.38.17:6379 192.168.38.27:6379 192.168.38.37:6379 192.168.38.47:6379 192.168.38.57:6379 192.168.38.67:6379 --cluster-replicas 1
redis集羣的節點維護

   集羣運行時間長久之後,難免由於硬件故障、網絡規劃、業務增長等原因對已有集羣進行相應的調整, 比如增加Redis node節點、減少節點、節點遷移、更換服務器等。

增加節點和刪除節點會涉及到已有的槽位重新分配及數據遷移

redis cluster查看狀態

  • redis-trib.rb 命令查看
[root@centos7-17 src]# redis-trib.rb info 192.168.38.17:6379
192.168.38.17:6379 (1de670f4...) -> 0 keys | 5461 slots | 1 slaves.
192.168.38.27:6379 (f76df109...) -> 0 keys | 5462 slots | 1 slaves.
192.168.38.37:6379 (c4b91bbf...) -> 0 keys | 5461 slots | 1 slaves.
127.0.0.1:6379> CLUSTER info
cluster_state: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:6
cluster_my_epoch:1
cluster_stats_messages_ping_sent:610
cluster_stats_messages_pong_sent:649
cluster_stats_messages_sent:1259
cluster_stats_messages_ping_received:644
cluster_stats_messages_pong_received:610
cluster_stats_messages_meet_received:5
cluster_stats_messages_received:1259

集羣維護之動態添加節點

增加Redis node節點,需要與之前的Redis node版本相同、配置一致,然後分別啓動兩臺Redis node,因爲一主一從。

  • 將192.168.38.77 導入至集羣中,然後對集羣槽位重新劃分。

添加節點到集羣:

  • redis-trib.rb add-node
add-node    new_host:new_port   existing_host:existing_port 
            要添加的新redis節點IP和端口 添加到的集羣中的master IP:端口,新的node節點加到集羣之後默認是 master節點,但是沒有slots數據,需要重新分配。
[root@centos7-17 src]# redis-trib.rb add-node 192.168.38.77:6379 192.168.38.17:6379

重新分配槽位:

  • 注:重新分配槽位時,每個節點均不能有數據
  • redis-trib.rb reshard host:port
[root@centos7-17 src]# redis-trib.rb  reshard 192.168.38.17:6379
.....
[OK] All nodes agree about slots configuration. 
>>> 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? 886338acd50c3015be68a760502b239f4509881c     #接收slot的服務器ID 

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                                                         #將哪些源主機的槽位進行重分

==> all是自動在所有的redis node選擇劃分,如果是從redis cluster刪除主機可以使用此方式將主機上的槽位全部移動到別的redis主機

當前集羣:
[root@centos7-17 src]# redis-trib.rb info 192.168.38.17:6379
192.168.38.17:6379 (1de670f4...) -> 0 keys | 4096 slots | 1 slaves.
192.168.38.27:6379 (f76df109...) -> 0 keys | 4096 slots | 1 slaves.
192.168.38.37:6379 (c4b91bbf...) -> 0 keys | 4096 slots | 1 slaves.
192.168.38.77:6379 (95c1150d...) -> 0 keys | 4096 slots | 0 slaves.

爲新的master添加slave節點

  • 剛添加和重新分配槽位的192.168.38.77主機沒有slave節點,因此爲了實現高可用,需要給其增加一個slave節點
[root@centos7-17 src]# redis-trib.rb add-node 192.168.38.87:6379 192.168.38.27:6379

[root@centos7-17 src]# redis-cli -h 192.168.38.87
192.168.38.87:6379> AUTH 123456
OK
192.168.38.87:6379> CLUSTER REPLICATE 95c1150d5910e2dd0950acda55383e1616b4c589
OK
  • 此時每個節點均有一個slave
  • redis-trib.rb info 192.168.38.17:6379
[root@centos7-17 ~]# redis-trib.rb info 192.168.38.17:6379
192.168.38.17:6379 (1de670f4...) -> 0 keys | 4096 slots | 1 slaves.
192.168.38.27:6379 (f76df109...) -> 0 keys | 4096 slots | 1 slaves.
192.168.38.87:6379 (493aca4e...) -> 0 keys | 4096 slots | 1 slaves.
192.168.38.77:6379 (95c1150d...) -> 0 keys | 4096 slots | 1 slaves.
[OK] 0 keys in 4 masters.
0.00 keys per slot on average.
[root@centos7-17 ~]#

集羣維護之動態刪除節點

   添加節點的時候是先添加node節點到集羣,然後分配槽位,刪除節點的操作與添加節點的操作正好相反,是先將被刪除的Redis node上的槽位遷移到集羣中的其他Redis node節點上,然後再將其刪除,如果一個Redis node節點上的槽位沒有被完全遷移,刪除該node的時候會提示有數據且無法刪除。

  • ①先將被刪除的Redis node上的槽位遷移
  • ②將下線節點從集羣中刪除

集羣節點下線:

①將新機加入節點
[root@centos7-17 src]# redis-trib.rb add-node 192.168.38.87:6379 192.168.38.17:6379


②槽位遷移至新機
[root@centos7-17 src]# redis-trib.rb  reshard 192.168.38.17:6379
    將192.168.38.37的4096個槽位遷至192.168.38.87

  • 從集羣刪除服務器: redis-trib.rb del-node host:port node_id
③槽位遷移之後,服務器IP信息還在集羣當中,因此還需要將IP信息從集羣刪除

[root@centos7-17 src]# redis-trib.rb del-node 192.168.38.37:6379 c4b91bbfc83610901126e83b0f174bca5d722799
>>> Removing node c4b91bbfc83610901126e83b0f174bca5d722799 from cluster 192.168.38.37:6379
>>> Sending CLUSTER FORGET messages to the cluster...
>>> SHUTDOWN the node.
[root@centos7-17 src]#
  • redis-trib.rb check 192.168.38.17:6379
[root@centos7-17 src]# redis-trib.rb check 192.168.38.17:6379
>>> Performing Cluster Check (using node 192.168.38.17:6379)
M: 1de670f4c8cccfd4670a643a32748cc53c5d0f2d 192.168.38.17:6379
   slots:1365-5460 (4096 slots) master
   1 additional replica(s)
S: b36dd413b27985ebadba6274dd2a3d1c076f97cd 192.168.38.67:6379
   slots: (0 slots) slave
   replicates f76df1092d5775f72196e8039be7843d3fbb8955
M: f76df1092d5775f72196e8039be7843d3fbb8955 192.168.38.27:6379
   slots:6827-10922 (4096 slots) master
   1 additional replica(s)
S: 6be42ff12a9f0dd89c98327784c0e281e72fb142 192.168.38.47:6379
   slots: (0 slots) slave
   replicates 493aca4ed8b6faf452781f8c078c8c6063086b79
M: 493aca4ed8b6faf452781f8c078c8c6063086b79 192.168.38.87:6379
   slots:12288-16383 (4096 slots) master
   1 additional replica(s)
S: 78f0bc9884fe3158178bf3b42e8d653639e39281 192.168.38.57:6379
   slots: (0 slots) slave
   replicates 1de670f4c8cccfd4670a643a32748cc53c5d0f2d
M: 95c1150d5910e2dd0950acda55383e1616b4c589 192.168.38.77:6379
   slots:0-1364,5461-6826,10923-12287 (4096 slots) master
   0 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
[root@centos7-17 src]#

==> 至此192.168.38.37 redis node已經下線(有個shutdown),然後與redis集羣無關了。

數據導入
將外部redis數據導入集羣
  • redis-trib.rb import --from 源 --replace cluster
# redis-trib.rb import --from 172.18.200.107:6382 --replace 192.168.38.17:6379
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章