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迁移都是这么做的了
在这里插入图片描述

缩容

懒得写, 类似增容的反方向。

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