Redis Cluster集羣增容和縮容

前言

本文以手動操作redis cluster集羣爲例, 講解並證明redis cluster的增容和縮容。
redis分三種模式:

  • 主從
  • 主從 + Sentinel(哨兵)
  • cluster

主從模式不是分佈式一致性裏面的主從,需要手動指定Mater(在slave機器上通過slaveof命令指定)。主負責讀寫,從負責讀。

  • 做到了讀寫分離。
  • 主從數據完全一致。
  • 主掛了就得手動切換從, 通常互聯網網絡環境, 得手動切換DNS。

針對主動模式發生宕機,需要手動切換主, Sentinel通過集羣監控, 來修復這個問題。


cluster模式, redis 3.0提供,非一致性hash(https://www.jianshu.com/p/e968c081f563), 使用slots槽

基於slots槽的redis cluster增容

slots基本概念

筆者的理解, 類似於Java的ConcurrentHash(JDK1.8-ConcurrentHashMap的 rehash 擴容邏輯
slots類似於Java ConcurrentHash裏的Node(僅僅是像,數據結構完全不一樣), 遷移的時候逐個slot遷移, 這樣集羣的可用性可得到保障。

Redis cluster 使用 slot 存儲數據。默認16384個。
存儲的數據, 存儲位置在:CRC16(key) mod 16384的值上, 裏面又會有細分數據結構。例如kvHash

unsigned int keyHashSlot(char *key, int keylen) {
    int s, e; /* start-end indexes of { and } */
    for (s = 0; s < keylen; s++)
        if (key[s] == '{') break;
    if (s == keylen) return crc16(key,keylen) & 0x3FFF;
    for (e = s+1; e < keylen; e++)
        if (key[e] == '}') break;
    if (e == keylen || e == s+1) return crc16(key,keylen) & 0x3FFF;
    return crc16(key+s+1,e-s-1) & 0x3FFF;
}

slot 在redis結點上的位置並不固定

在redis集羣中, 默認會分配連續的slot空間給每個結點。但是這只是默認分配而已, 後續擴容的時候會做調整(例如下文,7臺機器的集羣, slot大致連續, 但是當slot遷移到新增服務器後, 每臺的slot數字就不連續了):

127.0.0.1:7006> cluster slots
1) 1) (integer) 0
   2) (integer) 865
   3) 1) "127.0.0.1"
      2) (integer) 7000
      3) "ecee2fce5ddc618ad4e9d1738eb546653f4abd6f"
   4) 1) "127.0.0.1"
      2) (integer) 7001
      3) "4dd154308c6af771857d82de5785d0fedc32a224"
2) 1) (integer) 867
   2) (integer) 6666
   3) 1) "127.0.0.1"
      2) (integer) 7000
      3) "ecee2fce5ddc618ad4e9d1738eb546653f4abd6f"
   4) 1) "127.0.0.1"
      2) (integer) 7001
      3) "4dd154308c6af771857d82de5785d0fedc32a224"
3) 1) (integer) 6667
   2) (integer) 12306
   3) 1) "127.0.0.1"
      2) (integer) 7002
      3) "08da27b70687df11f480aed5d6e6dc58f0aa819e"
   4) 1) "127.0.0.1"
      2) (integer) 7003
      3) "c04d2e2a661dba2624e0c84c237bd24aa418d1c3"
4) 1) (integer) 12307
   2) (integer) 16383
   3) 1) "127.0.0.1"
      2) (integer) 7004
      3) "666868d5d51e060de165ec52d412926d1404af5a"
   4) 1) "127.0.0.1"
      2) (integer) 7005
      3) "800552c4c96fd79dbf8bbf5ff37fb4174d4a166b"
5) 1) (integer) 866
   2) (integer) 866
   3) 1) "127.0.0.1"
      2) (integer) 7006
      3) "22e6f70ab45662aef226d83c6b4a7b2eabb28ad1"
   4) 1) "127.0.0.1"
      2) (integer) 7007
      3) "62c5b7b83fcd86e7861a32c25d0d87c3998c5440"

slot遷移四步走

redis擴容設計巧妙, 擴容遷移即爲slot遷移. 只是承擔slot的機器變多了,slot槽本身結構並無變化

  • 針對待遷移的slot槽, 原機器設定爲導出
  • 針對待遷移的slot槽, 目標機器設定爲導入
  • 執行數據遷移, 此時所有對該遷移中的slot槽請求不可用, 轉爲ask狀態
  • 周知集羣其它結點,slot槽被遷移到了新的機器

四步走理解之後就很簡單。 相較於一致性hash算法, 影響面大大降低(單個slot槽), 平滑程度增加(簡單的槽遷移)。

在遷移過程中, 如果訪問到正在被遷移中的slot槽redis返回ASK轉向。
集羣本身會維護slot槽對應關係, 每個客戶端的實現, 也大多會維護這個映射關係。

搭建redis cluster, 並手動操作增容

下載redis安裝

下載在redis官網 redis.io

wget http://download.redis.io/releases/redis-5.0.4.tar.gz

安裝make && make install

make -j && make install

它會要求你運行make test, 可以嘗試。
最好將其拷貝到你期望的目錄中, 例如我默認喜歡/opt

mv * /opt/redis-5.0.4/

重點的兩個文件redis-cliredis-serversrc 目錄下

➜  conf ls /opt/redis-5.0.4/src/redis-cli
/opt/redis-5.0.4/src/redis-cli
➜  conf ls /opt/redis-5.0.4/src/redis-server 
/opt/redis-5.0.4/src/redis-server

啓動很簡單, ./redis-server /opt/redis-5.0.4/redis.conf (redis.conf默認目錄在根目錄下)
通過ps -ef | grep redis 可看到進程。

啓動多個redis

純手動操作, 新建一個redis的運行文件夾, 例如名叫cluster-test, 在它下面建立三個文件夾:

  • conf
  • data
  • logs
➜  conf ls /opt/redis-5.0.4/cluster-test 
conf data logs

進入conf 目錄操作, 你需要很多的redis-conf, 例如
你期望啓動8個redis進程組成cluster
偶數爲master
基數爲slave
先啓動6臺組成cluster, 三主三從, 然後啓動兩臺作爲增容測試。

從端口7000開始:


vim redis-7000.conf

## 指定運行端口
port 7000
daemonize yes
dir "/opt/redis-5.0.4/cluster-test/data"
logfile "/opt/redis-5.0.4/cluster-test/logs/7000.log"

#dbfilename不能配置爲路徑
dbfilename "dump-7000.rdb"

cluster-enabled yes

cluster-config-file nodes-7000.conf

#是否需要每個節點都可用,集羣纔算可用,關閉
cluster-require-full-coverage no

然後通過如下命令生成8個配置文件;

sed "s/7000/7001/g" redis-7000.conf > redis-7001.conf
sed "s/7000/7002/g" redis-7000.conf > redis-7002.conf
sed "s/7000/7003/g" redis-7000.conf > redis-7003.conf
sed "s/7000/7004/g" redis-7000.conf > redis-7004.conf
sed "s/7000/7005/g" redis-7000.conf > redis-7005.conf
sed "s/7000/7006/g" redis-7000.conf > redis-7006.conf
sed "s/7000/7007/g" redis-7000.conf > redis-7007.conf

再分別通過每個命令啓動服務

./redis-server ../cluster-test/conf/ redis-7000.conf

啓動完八個redis之後則可以看到全部redis進程:
在這裏插入圖片描述

組裝redis cluster

簡單幾個命令:
登陸某臺機器:

➜  src ./redis-cli -p 7000 
127.0.0.1:7000> cluster meet 127.0.0.1 7001
OK

依次對其他7002 7003 7004 7005機器執行該命令即可(暫時不要執行6、7兩臺, 他們作爲擴容用
此時查詢集羣狀態:

127.0.0.1:7000> cluster info
cluster_state:fail(如果你啓動過一次,並且分配了slot, data目錄有數據,再次啓動集羣,此處將是ok)
cluster_slots_assigned:0
cluster_slots_ok:0
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:1
cluster_size:0
cluster_current_epoch:0
cluster_my_epoch:0
cluster_stats_messages_sent:0
cluster_stats_messages_received:0

然後配置主從模式執行 cluster nodes查詢id:

127.0.0.1:7000> cluster nodes
62c5b7b83fcd86e7861a32c25d0d87c3998c5440 127.0.0.1:7007@17007 master - 0 1589044372000 7 connected
22e6f70ab45662aef226d83c6b4a7b2eabb28ad1 127.0.0.1:7006@17006 master - 0 1589044373669 5 connected
08da27b70687df11f480aed5d6e6dc58f0aa819e 127.0.0.1:7002@17002 master - 0 1589044374679 2 connected
c04d2e2a661dba2624e0c84c237bd24aa418d1c3 127.0.0.1:7003@17003 master - 0 1589044372664 3 connected
ecee2fce5ddc618ad4e9d1738eb546653f4abd6f 127.0.0.1:7000@17000 myself,master - 0 1589044369000 0 connected
800552c4c96fd79dbf8bbf5ff37fb4174d4a166b 127.0.0.1:7005@17005 master - 0 1589044373000 4 connected
666868d5d51e060de165ec52d412926d1404af5a 127.0.0.1:7004@17004 master - 0 1589044374000 6 connected
4dd154308c6af771857d82de5785d0fedc32a224 127.0.0.1:7001@17001 master - 0 1589044375690 1 connected

然後使用命令設定主從(表示將7001設置爲7000的從, 下面的id對應7000的id):

./redis-cli -p 7001 cluster replicate ecee2fce5ddc618ad4e9d1738eb546653f4abd6f

在這裏插入圖片描述
如上操作的效果就是:
在這裏插入圖片描述
你需要給每個redis進程分配slot

/opt/redis-5.0.4/src/redis-cli -p 7000 cluster addslots 0

因爲要分配16384個, 你可以使用腳本來分配vim addslots.sh

start=$1
end=$2
port=$3

for slot in `seq ${start} ${end}`
do
    /opt/redis-5.0.4/src/redis-cli -p ${port} cluster addslots ${slot}
done

然後執行:

./addslots.sh 0 6666 7000

表示將0~6666這麼多號槽分配給端口爲7000這個進程。
在這裏插入圖片描述

如上, 你的redis cluster搭建完畢, 三主三從, slot也分配完畢, 可以接活了:

127.0.0.1:7000> set hello world
OK
127.0.0.1:7000> set hello1 world
(error) MOVED 11613 127.0.0.1:7002
127.0.0.1:7000> set hello2 world
(error) MOVED 7486 127.0.0.1:7002
127.0.0.1:7000> set hello3 world
OK
127.0.0.1:7000> get hello
"world"
127.0.0.1:7000> get hello1
(error) MOVED 11613 127.0.0.1:7002
127.0.0.1:7000> 
127.0.0.1:7000> cluster keyslot hello
(integer) 866
127.0.0.1:7000> cluster keyslot hello1
(integer) 11613

擴容實操

目前有7000~7007八個redis進程, 前六個三主三從。 準備加入 7006

127.0.0.1:7006> cluster meet 127.0.0.1 7000
OK

然後開始遷移866這個slot(就是hello)。
1.在導入機器使用importing將目標slot設置爲導入狀態,id爲原進程的id

127.0.0.1:7006> CLUSTER SETSLOT 866 importing ecee2fce5ddc618ad4e9d1738eb546653f4abd6f
OK

2.在導出機器使用migrating將目標slot設置爲導出狀態,id爲被導入機器的id

127.0.0.1:7000> CLUSTER SETSLOT 866 migrating  22e6f70ab45662aef226d83c6b4a7b2eabb28ad1
OK

3.在導出機器執行migrate數據遷移

127.0.0.1:7000> MIGRATE 127.0.0.1 7006 "" 0 1000 keys hello
OK

4.最後,在任意機器執行告知slot遷移動作

127.0.0.1:7006> CLUSTER SETSLOT 866 node 22e6f70ab45662aef226d83c6b4a7b2eabb28ad1
OK

導出成功, 之後的每個slot遷移都是這麼做的了
在這裏插入圖片描述

縮容

懶得寫, 類似增容的反方向。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章