天天看點

redis叢集分片存儲redis叢集分片存儲

文章目錄

  • redis叢集分片存儲
      • 為什麼要分片存儲
      • 官方叢集方案
      • 搭建叢集
      • 叢集關心的問題
      • Java用戶端代碼
      • 非官方叢集方案

redis叢集分片存儲

為什麼要分片存儲

假設公司使用者有3千萬,使用者基本資訊緩存到redis中,需要記憶體10G,如何設計redis的緩存架構?

  1. 3千萬使用者,各種業務場景對使用者資訊的通路量很大,單台redis執行個體的讀寫瓶頸凸顯。
  2. 單redis執行個體管理10G記憶體,必然影響處理效率。
  3. redis的記憶體需求可能超過機器的最大記憶體,一台機器不夠用。

是以就有了分片存儲的技術。

官方叢集方案

redis cluster是Redis的分布式叢集解決方案,在3.0版本推出後有效地解決了redis分布式方面的需求,實作了資料在多個redis節點之間自動分片,故障自動轉移,擴容機制等功能。

redis叢集分片存儲redis叢集分片存儲
redis叢集分片存儲redis叢集分片存儲

搭建叢集

  1. 環境資訊

    centos6,redis5。

  2. 叢集的資訊

    我會在192.168.1.120,192.168.1.122,192.168.1.125這三台機器上分别啟動兩個redis執行個體,端口分别是6379和6380。

  3. 搭建叢集前確定每台機器上都已經安裝過redis,我的每台機器上的redis安裝目錄為:/var/redis-5.0.5。
  4. 每台機器上準備兩個redis配置檔案,一個redis_6379.conf,一個redis_6380.conf。
    # 配置檔案進行了精簡,完整配置可自行和官方提供的完整conf檔案進行對照。端口号自行對應修改
    #背景啟動的意思
    daemonize yes 
     #端口号
    port 6381
    # IP綁定,redis不建議對公網開放,直接綁定0.0.0.0沒毛病
    bind 0.0.0.0
    # redis資料檔案存放的目錄
    dir /usr/local/redis/data
    # 開啟AOF
    appendonly yes
     # 開啟叢集
    cluster-enabled yes
    # 會自動生成在上面配置的dir目錄下
    cluster-config-file nodes-6381.conf 
    cluster-node-timeout 5000
    # 這個檔案會自動生成
    pidfile /var/run/redis_6381.pid 
               
  5. 啟動6個redis執行個體,在三台機器的redis的安裝目錄的src下,分别運作如下兩條指令。
    ./redis-server ../redis_6379.conf
    ./redis-server ../redis_6380.conf
               
    使用ps -ef | grep redis指令檢視叢集是否啟動正常。
    redis叢集分片存儲redis叢集分片存儲
    redis叢集分片存儲redis叢集分片存儲
    redis叢集分片存儲redis叢集分片存儲
  6. 建立cluster,在redis安裝目錄的src下執行下面的指令。

    在已經使用安裝并使用過的redis伺服器上運作此指令可能會抛出如下錯誤。參考https://blog.csdn.net/XIANZHIXIANZHIXIAN/article/details/82777767解決。

    運作成功後會讓你确認叢集資訊。

    [root@spark src]# ./redis-cli --cluster create 192.168.1.120:6379 192.168.1.120:6380 192.168.1.122:6379 192.168.1.122:6380 192.168.1.125:6379 192.168.1.125:6380 --cluster-replicas 1
    >>> Performing hash slots allocation on 6 nodes...
    Master[0] -> Slots 0 - 5460
    Master[1] -> Slots 5461 - 10922
    Master[2] -> Slots 10923 - 16383
    Adding replica 192.168.1.122:6380 to 192.168.1.120:6379
    Adding replica 192.168.1.125:6380 to 192.168.1.122:6379
    Adding replica 192.168.1.120:6380 to 192.168.1.125:6379
    M: fd36242496e7a04d883857891ce425a0fc948783 192.168.1.120:6379
       slots:[0-5460] (5461 slots) master
    S: 9ffd15c5674f4ff06ff4dca186fbf919a992c87b 192.168.1.120:6380
       replicates ace69b140e315ce90ed6c19de1aaec73032b1ca6
    M: 77e054f92028a383b69147f0c21e680ae664bb6b 192.168.1.122:6379
       slots:[5461-10922] (5462 slots) master
    S: 1892d6272a759f454548ce97b0d09c580f687ef3 192.168.1.122:6380
       replicates fd36242496e7a04d883857891ce425a0fc948783
    M: ace69b140e315ce90ed6c19de1aaec73032b1ca6 192.168.1.125:6379
       slots:[10923-16383] (5461 slots) master
    S: a4c630716cd013da8a2e2ce89a26127c57ff356e 192.168.1.125:6380
       replicates 77e054f92028a383b69147f0c21e680ae664bb6b
    Can I set the above configuration? (type 'yes' to accept): y
    [root@spark src]# ./redis-cli -c -h 192.168.1.120 -p 6379 cluster nodes
    fd36242496e7a04d883857891ce425a0fc948783 :6379@16379 myself,master - 0 0 0 connected
    [root@spark src]# ./redis-cli -h 192.168.1.120 -p 6379
    192.168.1.120:6379> cluster nodes
    fd36242496e7a04d883857891ce425a0fc948783 :6379@16379 myself,master - 0 0 0 connected
    192.168.1.120:6379> 
    [root@spark src]# ./redis-cli --cluster create 192.168.1.120:6379 192.168.1.120:6380 192.168.1.122:6379 192.168.1.122:6380 192.168.1.125:6379 192.168.1.125:6380 --cluster-replicas 1
    >>> Performing hash slots allocation on 6 nodes...
    Master[0] -> Slots 0 - 5460
    Master[1] -> Slots 5461 - 10922
    Master[2] -> Slots 10923 - 16383
    Adding replica 192.168.1.122:6380 to 192.168.1.120:6379
    Adding replica 192.168.1.125:6380 to 192.168.1.122:6379
    Adding replica 192.168.1.120:6380 to 192.168.1.125:6379
    M: fd36242496e7a04d883857891ce425a0fc948783 192.168.1.120:6379
       slots:[0-5460] (5461 slots) master
    S: 9ffd15c5674f4ff06ff4dca186fbf919a992c87b 192.168.1.120:6380
       replicates ace69b140e315ce90ed6c19de1aaec73032b1ca6
    M: 77e054f92028a383b69147f0c21e680ae664bb6b 192.168.1.122:6379
       slots:[5461-10922] (5462 slots) master
    S: 1892d6272a759f454548ce97b0d09c580f687ef3 192.168.1.122:6380
       replicates fd36242496e7a04d883857891ce425a0fc948783
    M: ace69b140e315ce90ed6c19de1aaec73032b1ca6 192.168.1.125:6379
       slots:[10923-16383] (5461 slots) master
    S: a4c630716cd013da8a2e2ce89a26127c57ff356e 192.168.1.125:6380
       replicates 77e054f92028a383b69147f0c21e680ae664bb6b
    Can I set the above configuration? (type 'yes' to accept): yes
    >>> Nodes configuration updated
    >>> Assign a different config epoch to each node
    >>> Sending CLUSTER MEET messages to join the cluster
    Waiting for the cluster to join
    ...
    >>> Performing Cluster Check (using node 192.168.1.120:6379)
    M: fd36242496e7a04d883857891ce425a0fc948783 192.168.1.120:6379
       slots:[0-5460] (5461 slots) master
       1 additional replica(s)
    M: ace69b140e315ce90ed6c19de1aaec73032b1ca6 192.168.1.125:6379
       slots:[10923-16383] (5461 slots) master
       1 additional replica(s)
    S: 9ffd15c5674f4ff06ff4dca186fbf919a992c87b 192.168.1.120:6380
       slots: (0 slots) slave
       replicates ace69b140e315ce90ed6c19de1aaec73032b1ca6
    S: a4c630716cd013da8a2e2ce89a26127c57ff356e 192.168.1.125:6380
       slots: (0 slots) slave
       replicates 77e054f92028a383b69147f0c21e680ae664bb6b
    S: 1892d6272a759f454548ce97b0d09c580f687ef3 192.168.1.122:6380
       slots: (0 slots) slave
       replicates fd36242496e7a04d883857891ce425a0fc948783
    M: 77e054f92028a383b69147f0c21e680ae664bb6b 192.168.1.122:6379
       slots:[5461-10922] (5462 slots) master
       1 additional replica(s)
    [OK] All nodes agree about slots configuration.
    >>> Check for open slots...
    >>> Check slots coverage...
    [OK] All 16384 slots covered.
    
               
    1. 叢集檢驗和測試

      檢視所有節點資訊。

      [root@spark src]# ./redis-cli -c -h 192.168.1.120 -p 6379 cluster nodes
      ace69b140e315ce90ed6c19de1aaec73032b1ca6 192.168.1.125:6379@16379 master - 0 1561421002080 5 connected 10923-16383
      9ffd15c5674f4ff06ff4dca186fbf919a992c87b 192.168.1.120:6380@16380 slave ace69b140e315ce90ed6c19de1aaec73032b1ca6 0 1561421000463 5 connected
      fd36242496e7a04d883857891ce425a0fc948783 192.168.1.120:6379@16379 myself,master - 0 1561421000000 1 connected 0-5460
      a4c630716cd013da8a2e2ce89a26127c57ff356e 192.168.1.125:6380@16380 slave 77e054f92028a383b69147f0c21e680ae664bb6b 0 1561421000160 6 connected
      1892d6272a759f454548ce97b0d09c580f687ef3 192.168.1.122:6380@16380 slave fd36242496e7a04d883857891ce425a0fc948783 0 1561420999891 4 connected
      77e054f92028a383b69147f0c21e680ae664bb6b 192.168.1.122:6379@16379 master - 0 1561421001081 3 connected 5461-10922
      
      # 開啟用戶端不加-c選項,如果目前資料不應該在此執行個體上會傳回MOVED
      ./redis-cli -h 192.168.1.120 -p 6379
      192.168.1.120:6379> set a 1
      (error) MOVED 15495 192.168.1.125:6379
      192.168.1.120:6379> 
      
      # 開啟的話會為目前的資料自動跳轉到對應的執行個體
      [root@spark src]# ./redis-cli -h 192.168.1.120 -p 6379
      192.168.1.120:6379> set a 1
      (error) MOVED 15495 192.168.1.125:6379
      192.168.1.120:6379> 
      [root@spark src]# ./redis-cli -c -h 192.168.1.120 -p 6379
      192.168.1.120:6379> set a 1
      -> Redirected to slot [15495] located at 192.168.1.125:6379
      OK
      192.168.1.125:6379> get a
      "1"
      192.168.1.125:6379> set hello tony
      -> Redirected to slot [866] located at 192.168.1.120:6379
      OK
      192.168.1.120:6379> get a
      -> Redirected to slot [15495] located at 192.168.1.125:6379
      "1"
      192.168.1.125:6379> cluster keyslot a
      (integer) 15495
      192.168.1.125:6379> 
                 
    2. 叢集slot數量整理reshard
      # 有時根據叢集中的機器的配置,我們可能希望有些性能高的機器上的槽多一些
      # 就可以使用reshard,下面我們從192.168.1.120伺服器移動1000個slot到192.168.1.122
      # 這台伺服器上
      ./redis-cli --cluster reshard 192.168.1.120:6379 --cluster-from fd36242496e7a04d883857891ce425a0fc948783 --cluster-to 77e054f92028a383b69147f0c21e680ae664bb6b --cluster-slots 1000 --cluster-yes
      
      # 運作後會有如下輸出
      Moving slot 714 from 192.168.1.120:6379 to 192.168.1.122:6379: 
      Moving slot 715 from 192.168.1.120:6379 to 192.168.1.122:6379: 
      
      # 重新檢查叢集
      ./redis-cli --cluster check 192.168.1.120:6379
          
      M: fd36242496e7a04d883857891ce425a0fc948783 192.168.1.120:6379
         slots:[1000-5460] (4461 slots) master
         1 additional replica(s)
      M: ace69b140e315ce90ed6c19de1aaec73032b1ca6 192.168.1.125:6379
         slots:[10923-16383] (5461 slots) master
         1 additional replica(s)
      S: 9ffd15c5674f4ff06ff4dca186fbf919a992c87b 192.168.1.120:6380
         slots: (0 slots) slave
         replicates ace69b140e315ce90ed6c19de1aaec73032b1ca6
      S: a4c630716cd013da8a2e2ce89a26127c57ff356e 192.168.1.125:6380
         slots: (0 slots) slave
         replicates 77e054f92028a383b69147f0c21e680ae664bb6b
      S: 1892d6272a759f454548ce97b0d09c580f687ef3 192.168.1.122:6380
         slots: (0 slots) slave
         replicates fd36242496e7a04d883857891ce425a0fc948783
      M: 77e054f92028a383b69147f0c21e680ae664bb6b 192.168.1.122:6379
         slots:[0-999],[5461-10922] (6462 slots) master
         1 additional replica(s)
      [OK] All nodes agree about slots configuration.
      >>> Check for open slots...
      >>> Check slots coverage...
      [OK] All 16384 slots covered.
      # 可以看出從192.168.1.120移動了1000個slot到192.168.1.122
                 
    3. 測試自動故障轉移
      # cluster叢集不保證資料一緻,資料也可能丢失
      # 首先是運作用戶端不斷的寫入或讀取資料,以便能夠發現問題
      # 然後是模拟節點故障:找一個主節點關閉,主從故障切換的過程中,這個時間端的操作,用戶端而言,隻能是失敗
      # 官方描述 https://redis.io/topics/cluster-spec  
      There is always a window of time when it is possible to lose writes during partitions.
      分區的時間視窗内總是有可能丢失寫操作。
      
      # 将192.168.1.120:6379這個執行個體停掉,192.168.1.122:6380成為一個master
       ./redis-cli -c -h 192.168.1.120 -p 6379
      192.168.1.120:6379> shutdown
      not connected> set a 1 [expiration EX seconds|PX milliseconds] [NX|XX]
      [root@spark src]# ./redis-cli --cluster check 192.168.1.120:6380
      Could not connect to Redis at 192.168.1.120:6379: Connection refused
      192.168.1.125:6379 (ace69b14...) -> 1 keys | 5461 slots | 1 slaves.
      192.168.1.122:6380 (1892d627...) -> 0 keys | 4461 slots | 0 slaves.
      192.168.1.122:6379 (77e054f9...) -> 1 keys | 6462 slots | 1 slaves.
      [OK] 2 keys in 3 masters.
      0.00 keys per slot on average.
      >>> Performing Cluster Check (using node 192.168.1.120:6380)
      S: 9ffd15c5674f4ff06ff4dca186fbf919a992c87b 192.168.1.120:6380
         slots: (0 slots) slave
         replicates ace69b140e315ce90ed6c19de1aaec73032b1ca6
      M: ace69b140e315ce90ed6c19de1aaec73032b1ca6 192.168.1.125:6379
         slots:[10923-16383] (5461 slots) master
         1 additional replica(s)
      M: 1892d6272a759f454548ce97b0d09c580f687ef3 192.168.1.122:6380
         slots:[1000-5460] (4461 slots) master
      M: 77e054f92028a383b69147f0c21e680ae664bb6b 192.168.1.122:6379
         slots:[0-999],[5461-10922] (6462 slots) master
         1 additional replica(s)
      S: a4c630716cd013da8a2e2ce89a26127c57ff356e 192.168.1.125:6380
         slots: (0 slots) slave
         replicates 77e054f92028a383b69147f0c21e680ae664bb6b
      [OK] All nodes agree about slots configuration.
      >>> Check for open slots...
      >>> Check slots coverage...
      [OK] All 16384 slots covered.
      [root@spark src]# 
      
      # 檢視此時的叢集資訊,192.168.1.120:6379下線了
      [root@spark src]# ./redis-cli -c -h 192.168.1.120 -p 6380
      192.168.1.120:6380> cluster nodes
      fd36242496e7a04d883857891ce425a0fc948783 192.168.1.120:6379@16379 master,fail - 1561422523649 1561422523348 1 disconnected
      ace69b140e315ce90ed6c19de1aaec73032b1ca6 192.168.1.125:6379@16379 master - 0 1561422691644 5 connected 10923-16383
      1892d6272a759f454548ce97b0d09c580f687ef3 192.168.1.122:6380@16380 master - 0 1561422693669 8 connected 1000-5460
      77e054f92028a383b69147f0c21e680ae664bb6b 192.168.1.122:6379@16379 master - 0 1561422693570 7 connected 0-999 5461-10922
      9ffd15c5674f4ff06ff4dca186fbf919a992c87b 192.168.1.120:6380@16380 myself,slave ace69b140e315ce90ed6c19de1aaec73032b1ca6 0 1561422687000 2 connected
      a4c630716cd013da8a2e2ce89a26127c57ff356e 192.168.1.125:6380@16380 slave 77e054f92028a383b69147f0c21e680ae664bb6b 0 1561422692148 7 connected
      192.168.1.120:6380> 
      
      # 如果再把192.168.1.120:6379啟動起來,其會議salve的身份加入叢集
      [root@spark src]# ./redis-cli --cluster check 192.168.1.120:6380
      192.168.1.125:6379 (ace69b14...) -> 1 keys | 5461 slots | 1 slaves.
      192.168.1.122:6380 (1892d627...) -> 0 keys | 4461 slots | 1 slaves.
      192.168.1.122:6379 (77e054f9...) -> 1 keys | 6462 slots | 1 slaves.
      [OK] 2 keys in 3 masters.
      0.00 keys per slot on average.
      >>> Performing Cluster Check (using node 192.168.1.120:6380)
      S: 9ffd15c5674f4ff06ff4dca186fbf919a992c87b 192.168.1.120:6380
         slots: (0 slots) slave
         replicates ace69b140e315ce90ed6c19de1aaec73032b1ca6
      S: fd36242496e7a04d883857891ce425a0fc948783 192.168.1.120:6379
         slots: (0 slots) slave
         replicates 1892d6272a759f454548ce97b0d09c580f687ef3
      M: ace69b140e315ce90ed6c19de1aaec73032b1ca6 192.168.1.125:6379
         slots:[10923-16383] (5461 slots) master
         1 additional replica(s)
      M: 1892d6272a759f454548ce97b0d09c580f687ef3 192.168.1.122:6380
         slots:[1000-5460] (4461 slots) master
         1 additional replica(s)
      M: 77e054f92028a383b69147f0c21e680ae664bb6b 192.168.1.122:6379
         slots:[0-999],[5461-10922] (6462 slots) master
         1 additional replica(s)
      S: a4c630716cd013da8a2e2ce89a26127c57ff356e 192.168.1.125:6380
         slots: (0 slots) slave
         replicates 77e054f92028a383b69147f0c21e680ae664bb6b
      [OK] All nodes agree about slots configuration.
      >>> Check for open slots...
      >>> Check slots coverage...
      [OK] All 16384 slots covered.
      
                 
    4. 手動故障轉移
      # 可能某個節點需要維護(機器下線,硬體更新,系統版本調整等場景),需要手動的實作轉移
      # 在slave節點上執行指令
      CLUSTER FAILOVER
      # 相比較于自動故障轉移而言,手動故障轉移不會丢失資料
                 
      # 上面我們手動将192.168.1.120:6379上的redis服務停掉再起來後,它以slave的身份加入
      # 叢集,現在我們想讓它重新成為master
      [root@spark src]# ./redis-cli -c -h 192.168.1.120 -p 6379
      # 檢視目前叢集資訊
      192.168.1.120:6379> cluster nodes
      1892d6272a759f454548ce97b0d09c580f687ef3 192.168.1.122:6380@16380 master - 0 1561423733576 8 connected 1000-5460
      ace69b140e315ce90ed6c19de1aaec73032b1ca6 192.168.1.125:6379@16379 slave 9ffd15c5674f4ff06ff4dca186fbf919a992c87b 0 1561423734623 9 connected
      a4c630716cd013da8a2e2ce89a26127c57ff356e 192.168.1.125:6380@16380 slave 77e054f92028a383b69147f0c21e680ae664bb6b 0 1561423734084 7 connected
      9ffd15c5674f4ff06ff4dca186fbf919a992c87b 192.168.1.120:6380@16380 master - 0 1561423735107 9 connected 10923-16383
      77e054f92028a383b69147f0c21e680ae664bb6b 192.168.1.122:6379@16379 master - 0 1561423734620 7 connected 0-999 5461-10922
      fd36242496e7a04d883857891ce425a0fc948783 192.168.1.120:6379@16379 myself,slave 1892d6272a759f454548ce97b0d09c580f687ef3 0 1561423733000 1 connected
      # 執行手動故障轉移
      192.168.1.120:6379> cluster failover
      OK
      # 檢查手動故障轉移效果,發現192.168.1.120:6379再次成為master
      192.168.1.120:6379> cluster nodes
      1892d6272a759f454548ce97b0d09c580f687ef3 192.168.1.122:6380@16380 slave fd36242496e7a04d883857891ce425a0fc948783 0 1561423750936 10 connected
      ace69b140e315ce90ed6c19de1aaec73032b1ca6 192.168.1.125:6379@16379 slave 9ffd15c5674f4ff06ff4dca186fbf919a992c87b 0 1561423751000 9 connected
      a4c630716cd013da8a2e2ce89a26127c57ff356e 192.168.1.125:6380@16380 slave 77e054f92028a383b69147f0c21e680ae664bb6b 0 1561423750000 7 connected
      9ffd15c5674f4ff06ff4dca186fbf919a992c87b 192.168.1.120:6380@16380 master - 0 1561423751955 9 connected 10923-16383
      77e054f92028a383b69147f0c21e680ae664bb6b 192.168.1.122:6379@16379 master - 0 1561423750432 7 connected 0-999 5461-10922
      fd36242496e7a04d883857891ce425a0fc948783 192.168.1.120:6379@16379 myself,master - 0 1561423751000 10 connected 1000-5460
      192.168.1.120:6379> 
                 
    5. 擴容
      # 我們再在192.168.1.120這台伺服器上準備一個redis配置檔案,redis_6381.conf
      # 啟動新節點
      ./redis-server ../redis_6381.conf
      
      # 以master身份加入叢集
      ./redis-cli --cluster add-node 192.168.1.120:6381 192.168.1.120:6379
      >>> Adding node 192.168.1.120:6381 to cluster 192.168.1.120:6379
      >>> Performing Cluster Check (using node 192.168.1.120:6379)
      M: fd36242496e7a04d883857891ce425a0fc948783 192.168.1.120:6379
         slots:[1000-5460] (4461 slots) master
         1 additional replica(s)
      S: 1892d6272a759f454548ce97b0d09c580f687ef3 192.168.1.122:6380
         slots: (0 slots) slave
         replicates fd36242496e7a04d883857891ce425a0fc948783
      S: ace69b140e315ce90ed6c19de1aaec73032b1ca6 192.168.1.125:6379
         slots: (0 slots) slave
         replicates 9ffd15c5674f4ff06ff4dca186fbf919a992c87b
      S: a4c630716cd013da8a2e2ce89a26127c57ff356e 192.168.1.125:6380
         slots: (0 slots) slave
         replicates 77e054f92028a383b69147f0c21e680ae664bb6b
      M: 9ffd15c5674f4ff06ff4dca186fbf919a992c87b 192.168.1.120:6380
         slots:[10923-16383] (5461 slots) master
         1 additional replica(s)
      M: 77e054f92028a383b69147f0c21e680ae664bb6b 192.168.1.122:6379
         slots:[0-999],[5461-10922] (6462 slots) master
         1 additional replica(s)
      [OK] All nodes agree about slots configuration.
      >>> Check for open slots...
      >>> Check slots coverage...
      [OK] All 16384 slots covered.
      >>> Send CLUSTER MEET to node 192.168.1.120:6381 to make it join the cluster.
      [OK] New node added correctly.
      # 檢視此時的叢集資訊,新建立的redis執行個體以master身份加入叢集
      [root@spark src]# ./redis-cli -c -h 192.168.1.120 -p 6379
      192.168.1.120:6379> cluster nodes
      12593a9cfbdffd272547926b625acb09d306c7fc 192.168.1.120:6381@16381 master - 0 1561424739585 0 connected
      1892d6272a759f454548ce97b0d09c580f687ef3 192.168.1.122:6380@16380 slave fd36242496e7a04d883857891ce425a0fc948783 0 1561424739488 10 connected
      ace69b140e315ce90ed6c19de1aaec73032b1ca6 192.168.1.125:6379@16379 slave 9ffd15c5674f4ff06ff4dca186fbf919a992c87b 0 1561424740528 9 connected
      a4c630716cd013da8a2e2ce89a26127c57ff356e 192.168.1.125:6380@16380 slave 77e054f92028a383b69147f0c21e680ae664bb6b 0 1561424740529 7 connected
      9ffd15c5674f4ff06ff4dca186fbf919a992c87b 192.168.1.120:6380@16380 master - 0 1561424740628 9 connected 10923-16383
      77e054f92028a383b69147f0c21e680ae664bb6b 192.168.1.122:6379@16379 master - 0 1561424740019 7 connected 0-999 5461-10922
      fd36242496e7a04d883857891ce425a0fc948783 192.168.1.120:6379@16379 myself,master - 0 1561424738000 10 connected 1000-5460
      192.168.1.120:6379> 
      # 本質就是發送一個新節點通過CLUSTER MEET指令加入叢集
      # 注意新加入的節點是沒有配置設定slot的,它不會存儲資料,我們使用之前需要給它配置設定slot
      # 參考第8點
      
      # 以slave身份加入叢集
      ./redis-cli --cluster add-node 192.168.1.120:6381 192.168.1.120:6379 --cluster-slave
      # 還可以手動指定要以slave身份加入叢集的節點的master節點,否則就選擇一個slave數量
      # 最少的master
      ./redis-cli --cluster add-node 192.168.1.120:6381 192.168.1.120:6379 --cluster-slave --cluster-master-id <node-id>
      # 将空master轉換為slave
      cluster replicate <master-node-id>
                 
    6. 縮容
      # 注意:删除master的時候要把資料清空或者配置設定給其他主節點
      # 下面我們将新加入的192.168.1.120:6381節點删除
      [root@spark src]# ./redis-cli --cluster del-node 192.168.1.120:6381 12593a9cfbdffd272547926b625acb09d306c7fc
      >>> Removing node 12593a9cfbdffd272547926b625acb09d306c7fc from cluster 192.168.1.120:6381
      >>> Sending CLUSTER FORGET messages to the cluster...
      >>> SHUTDOWN the node.
      # 檢視6381對應的redis程序是否還在
      [root@spark src]# ps -ef | grep redis
      root     17475 17274  0 07:53 pts/1    00:00:34 ./redis-server 192.168.1.120:6380 [cluster]
      root     17503 17228  0 08:33 pts/0    00:00:18 ./redis-server 192.168.1.120:6379 [cluster]
      root     17571 17303  0 09:17 pts/3    00:00:00 grep redis
      [root@spark src]# 
                 

叢集關心的問題

  1. 增加了slot槽位的計算,是不是比單機性能差?

    共16384個槽位,slots槽計算方式公開的,HASH_SLOT = CRC(key) mod 16384。為了避免每次都需要伺服器計算重定向,優秀的java用戶端都實作了本地計算,并且緩存伺服器slots配置設定,有變動時再更新本地内容,進而避免了多次重定向帶來的性能損耗。

  2. redis叢集大小,到底可以裝多少資料?

    理論是可以做到16384個槽,每個槽對應一個執行個體,但是redis官方建議是最大1000個執行個體。

  3. 叢集節點間是怎麼通信的?

    每個redis叢集節點都有一個額外的TCP端口,每個節點使用TCP連接配接與其他節點連接配接。檢測和故障轉移這些步驟基本和哨兵模式類似。

  4. ask和moved重定向的差別。

    重定向包括兩種情況:

    • 若确定slot不屬于目前節點,redis會傳回moved。
    • 若目前redis節點正在處理slot遷移,則代表此處請求對應的key暫時不在此節點,傳回ask,告訴用戶端本次請求重定向。
  5. 資料傾斜和通路傾斜的問題。

    傾斜導緻叢集中部分節點資料多,壓力大。解決方案分為前期和後期:

    • 前期是業務層面提前預測,哪些是熱點key,在設計的過程中規避。
    • 後期是slot遷移,盡量将壓力分攤(slot調整有自動reblance,reshard和手動)。
  6. slot手動遷移怎麼做?
    • 在遷移目的節點執行cluster setslot IMPORTING 指令,指明需要遷移的slot和遷移源節點。
    • 在遷移源節點執行cluster setslot MIGRATING 指令,指明需要遷移的slot和遷移目的節點。
    • 在遷移源節點執行cluster getkeysinslot擷取該slot的key清單。
    • 在遷移源節點對每個key執行migrate指令,該指令會同步把該key遷移到目的節點。
    • 在遷移源節點傳回執行cluster getkeysinslot指令,直到該slot的清單為空。
    • 在遷移源節點和目的節點執行cluster setslot NODE ,完成遷移操作。
  7. 節點之間會交換資訊,傳遞的消息包括槽的資訊,帶來帶寬消耗。避免使用大的叢集,可以分多個叢集。
  8. Pub/Sub釋出訂閱機制,對叢集内任意一個節點執行publish釋出消息,這個消息會在叢集中進行傳播,其他節點接收到釋出的消息。
  9. 讀寫分離。
    • redis-cluster預設所有從節點上的讀寫,都會重定向到key對接槽的主節點上。
    • 可以通過readonly設定目前連接配接可讀,通過readwrite取消目前連接配接的可讀狀态。
    • 主從節點依然存在資料不一緻的問題。

Java用戶端代碼

  1. 配置Bean。
    @Configuration
    // 在cluster環境下生效
    @Profile("a7_cluster")
    class ClusterAppConfig {
        @Bean
        public JedisConnectionFactory redisConnectionFactory() {
            System.out.println("加載cluster環境下的redis client配置");
            RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration(Arrays.asList(
                    "192.168.1.120:6379",
                    "192.168.1.120:6380",
                    "192.168.1.122:6379",
                    "192.168.1.122:6380",
                    "192.168.1.125:6379",
                    "192.168.1.125:6380"
            ));
            // 自适應叢集變化
            return new JedisConnectionFactory(redisClusterConfiguration);
        }
    }
               
  2. 業務service。
    @Service
    @Profile("a7_cluster")
    public class ClusterService {
        @Autowired
        private StringRedisTemplate template;
    
        public void set(String userId, String userInfo) {
            template.opsForValue().set(userId, userInfo);
        }
    }
               
  3. 單元測試
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration("classpath:applicationContext.xml")
    @ActiveProfiles("a7_cluster") // 設定profile
    // 叢集對于用戶端而言,基本是無感覺的
    public class ClusterServiceTests {
        @Autowired
        ClusterService clusterService;
    
        @Test
        public void setTest() {
            clusterService.set("tony", "hahhhhh");
            clusterService.set("a", "1");
            clusterService.set("foo", "bar");
        }
    
        // 測試cluster叢集故障時的反應
        @Test
        public void failoverTest() {
            while (true) {
                try {
                    long i = System.currentTimeMillis();
                    clusterService.set("tony", i + "");
                    // delay 10ms
                    TimeUnit.MILLISECONDS.sleep(10);
                } catch (Exception e) {
                    System.out.println(e.getMessage());
                }
            }
        }
    }
    
    運作setTest後,它實際上存儲在192.168.1.120:6380
    ./redis-cli -c -h 192.168.1.120 -p 6379
    192.168.1.120:6379> get tony
    -> Redirected to slot [14405] located at 192.168.1.120:6380
    "hahhhhh"
    192.168.1.120:6380> get tongy
    -> Redirected to slot [9429] located at 192.168.1.122:6379
    (nil)
    192.168.1.122:6379> get tony
    -> Redirected to slot [14405] located at 192.168.1.120:6380
    "hahhhhh"
    192.168.1.120:6380
        
    運作failoverTest後,停掉192.168.1.120:6380
    192.168.1.120:6380> shutdown
    not connected> 
    對用戶端無影響
               

非官方叢集方案

  1. Codis,豌豆莢開源。
  2. twemproxy,推特開源。

繼續閱讀