Bootstrap

Redis 集群

集群

集群的基本介绍

哨兵 模式, 提⾼了系统的可⽤性. 但是真正⽤来存储数据的还是 master 和 slave 节点. 所有的数
据都需要存储在单个 master 和 slave 节点中.

如果数据量很⼤, 接近超出了 master / slave 所在机器的物理内存, 就可能出现严重问题了

虽然硬件价格在不断降低, ⼀些中⼤⼚的服务器内存已经可以达到 TB 级别了, 但是 1TB 在当
前这个 "⼤数据" 时代, 俨然不算什么, 有的时候我们确实需要更⼤的内存空间来保存更多的数
据。
 

 关于集群这个名词:

广义的集群:只要是多台机器构成了分布式系统,就可以称为是一个“集群”。之前介绍的主从结构,哨兵模式就可以称为是“广义的集群”。

狭义的集群:Redis提供的集群模式。在这个集群模式下,主要是为了解决存储空间不足的问题。

哨兵模式提高了系统的可用性,但是本质上还是redis主从节点上存储数据,这就要求了每个节点(主节点/从节点)都要存储这个数据的“全集”。因为Redis是内存存储数据的,一个服务器的内存大小很容易到达上限(即便现在的硬件在不断发展)。

Redis 的集群就是在上述的思路之下, 引⼊多组 Master / Slave , 每⼀组 Master / Slave 存储数据全集的⼀部分, 从⽽构成⼀个更⼤的整体, 称为 Redis 集群 (Cluster)。

假定整个数据全集是 1 TB, 引⼊三组 Master / Slave 来存储. 那么每⼀组机器只需要存储整个
数据全集的 1/3 即可.

  

这样把数据分成了多份,把每份数据称为 一个 “分片”。 

如果全量数据进一步增加,那么就只要跟着增加分片的数量即可。 

接下来就介绍三种主流的分片方式。

哈希求余算法

哈希求余的分片方式主要就是借鉴了哈希表的思想:
借助hash函数,把一个key映射到一个整数,再针对数组的长度求余,就可以得到一个数组下标。

 比如有三个分片的编号:0,1,2  。此时对于要插入数据的key进行一个hash算法(比如md5算法),计算出了hash值,然后再用这个hash值 模上分片的数量,就可以求出应该要把它放在哪一个分片上。将来如果要查找这个key,也是经过同样的步骤,先计算hash值,然后找到它在哪一个分片上,最后再在这个分片上查找即可。

关于md5算法:

md5本身就是一个计算hash值的算法,针对一个字符串里面的内容进行一系列的数学变换,将其转化成为一个十六进制的数字。

 md5是一个应用很广泛的hash算法。它的优点有如下:

1.md5计算的结果是定长的:无论输入的原字符串有多长,最终计算的结果都是固定长度。

2.md5计算的结果是分散的:两个原字符串,即便只是有一点地方不相同,那么最终计算出来的结果也是会相差很大的。

3.md5计算结果是不可逆的(加密):理论上是不能通过md5的值计算出原字符串是啥的。

虽然有一些md5破解,但其实无非就是将一些常用的字符串生成md5值,然后将这些值储存起来,然后按照打表的方式映射到原来的字符串。这其实就是一种暴力破解方式,是一种用空间换时间的方式,还是不大靠谱的。

 哈希求余算法存在一个很大的问题,那就是一旦服务器集群需要扩容,那么就需要更高的成本。

如上图可以看到, 整个扩容⼀共 21 个 key, 只有 3 个 key 没有经过搬运, 其他的 key 都是搬运过的 

虽然hash算法和key都没有改变,仅仅只是N变了,但是key最终映射的结果还是有较大概率不同

 一致性哈希算法

为了降低上述的搬运开销, 能够更⾼效扩容, 业界提出了 "⼀致性哈希算法".
key 映射到分⽚序号的过程不再是简单求余了, ⽽是改成以下过程:

第⼀步, 把 0 -> 2^32-1 这个数据空间, 映射到⼀个圆环上. 数据按照顺时针⽅向增⻓ 假设此时还是三个分片。

第⼆步, 假设当前存在三个分⽚, 就把分⽚放到圆环的某个位置上 

第三步, 假定有⼀个 key, 计算得到 hash 值 H, 那么这个 key 映射到哪个分⽚呢? 规则很简单, 就是从 H所在位置, 顺时针往下找, 找到的第⼀个分⽚, 即为该 key 所从属的分⽚

这就相当于, N 个分⽚的位置, 把整个圆环分成了 N 个管辖区间. Key 的 hash 值落在某个区间内, 就归对应区间管理。

在哈希求余算法中,搬运数据的成本之所以会很大,是因为计算当前key属于哪个分片,是交替的

比如:

 而在一致性哈希这样的设定下,把交替出现改为了连续出现。

此时当我们由三个分片扩容到四个分片时:

比如3号分片,我们只需要在原本属于0号分片的区间切分出一半给3号分片就可以了,那么此时的1号分片和2号分片的数据是不需要改动的,只需要将0号分片的数据搬运就可以了。

这样就大大减少了数据搬运的成本。

不过数据搬运的成本是变低了,但是又出现了一个新的问题,那就是这几个分片上的数据变得不均匀了,也就是数据倾斜问题。

虽然针对数据倾斜的问题,以上图为例,我们可以通过再加两批机器多分两片的方式,让数据变得均匀,这种方案是可行的,但是这样就会导致实际存储数据用不了这么多内存,导致内存浪费掉了

哈希槽分区算法

 哈希槽分区算法也是Redis真正采用的分片算法。

为了解决上述问题 (搬运成本⾼ 和 数据分配不均匀), Redis cluster 引⼊了哈希槽 (hash slots) 算法
hash_slot = crc16(key) % 16384

 其中这个crc16也是一种hash算法。

16384是指有16384个槽位,也就是16k的大小。

相当于是把整个哈希值, 映射到 16384 个槽位上, 也就是 [0, 16383].
然后再把这些槽位⽐较均匀的分配给每个分⽚. 每个分⽚的节点都需要记录⾃⼰持有哪些分⽚

假设当前有三个分⽚, ⼀种可能的分配⽅式:
0 号分⽚: [0, 5461], 共 5462 个槽位
1 号分⽚: [5462, 10923], 共 5462 个槽位
2 号分⽚: [10924, 16383], 共 5460 个槽位

注意:分配槽位的方式是很灵活的,并且每个分片的槽位也不一定连续。

如果需要进⾏扩容, ⽐如新增⼀个 3 号分⽚, 就可以针对原有的槽位进⾏重新分配.
⽐如可以把之前每个分⽚持有的槽位, 各拿出⼀点, 分给新分⽚.
⼀种可能的分配⽅式:
0 号分⽚: [0, 4095], 共 4096 个槽位
1 号分⽚: [5462, 9557], 共 4096 个槽位
2 号分⽚: [10924, 15019], 共 4096 个槽位
3 号分⽚: [4096, 5461] + [9558, 10923] + [15019, 16383], 共 4096 个槽位

在上述过程中,只有被移动的槽位,才需要进行数据搬运。 

这种算法本质其实就是把哈希求余算法和一致性哈希算法这两种方式结合了一下。

此外,每个分片都会用 “位图” 这样的数据结构来表示当前有多少槽位。

也就是使用16384个bit位,每一个bit位用 0/1来表示是否拥有这个槽位。

16384 / 8 / 1024 = 2,也就是2KB的数据大小。

 对于此处有两个常见的面试题:

问题⼀: Redis 集群是最多有 16384 个分⽚吗?

如果有16384个分片,也就意味着每一个分片只有一个槽位,此时是很难保证在各个分片上的均衡性的。因为key是要先映射到槽位,再由槽位映射到分片上的。

比如有的槽位可能是多个key,有的槽位可能没有key。

实际上 Redis 的作者建议集群分⽚数不应该超过 1000。
并且如果真的有1.6w个分片,那么这就是一个至少由几万台主机组成的集群,这样的集群本身的可用性就是一个很大的问题,系统越复杂,出现故障的概率就越高。

问题⼆: 为什么是 16384 个槽位? 

在github上有Redis作者的回答:

翻译过来⼤概意思是:
节点之间通过⼼跳包通信. ⼼跳包中包含了该节点持有哪些 slots. 这个是使⽤位图这样的数据结构
表⽰的. 表⽰ 16384 (16k) 个 slots, 需要的位图⼤⼩是 2KB. 如果给定的 slots 数更多了, ⽐如 65536个了, 此时就需要消耗更多的空间, 8 KB 位图表⽰了. 8 KB, 对于内存来说不算什么, 但是在频繁的⽹络⼼跳包中, 还是⼀个不⼩的开销的.
另⼀⽅⾯, Redis 集群⼀般不建议超过 1000 个分⽚. 所以 16k 对于最⼤ 1000 个分⽚来说是⾜够⽤
的, 同时也会使对应的槽位配置位图体积不⾄于很⼤.

简短总结就是:16384个槽位基本是够用的,同时占用的硬件资源(比如在网络带宽中)又不是很大。如果用65535差不多就是8kb的大小,虽然在内存上8kb比2kb大不了多少,但是心跳包是周期性通信的,就比较频繁的吃网络带宽资源。

docker搭建集群环境

因为这里我们只有一个云服务器,所以我们还是利用docker来搭建一个集群,在实际工作中一般是通过多台主机的方式来搭建集群的。

并且在搭建集群环境之前,需要先把之前的redis容器给停止掉。

 第一步:创建目录和配置
 

redis-cluster/
├── docker-compose.yml
└── generate.sh

其中,在linux上,.sh后缀结尾的文件称为 “shell脚本”。
使用命令操作,就比较适合把命令写到一个文件中,批量化执行,同时还能加入条件,循环,函数等机制,基于这些可以完成一些更复杂的工作。

这里我们先创建9个redis节点,然后再创建2个redis节点作为扩容节点,一共就是11个redis节点。

generate.sh 内容如下
for port in $(seq 1 9); \
do \
mkdir -p redis${port}/
touch redis${port}/redis.conf
cat << EOF > redis${port}/redis.conf
port 6379
bind 0.0.0.0
protected-mode no
appendonly yes
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.30.0.10${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
EOF
done


# 注意 cluster-announce-ip 的值有变化. 
for port in $(seq 10 11); \
do \
mkdir -p redis${port}/
touch redis${port}/redis.conf
cat << EOF > redis${port}/redis.conf
port 6379
bind 0.0.0.0
protected-mode no
appendonly yes
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.30.0.1${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
EOF
done

 关于这里的一些解释:

这里基于循环一共创建了11个redis的配置文件。 

写好后,执行这个脚本:

bash generate.sh

 

如期生成了11个目录,每个目录下就是一个redis的配置文件

每个配置文件里:

但是主要注意,这里我们配置的是静态id地址,比如上述的ip地址主机号101,网络号都是127.30.0 一共24位。

在shell脚本那里,我们就用了shell中字符拼接的方式,配置了主机号是递增的一系列ip地址。 

这些配置文件其实就是 cluster-announce-ip不同,其它都是一样的。

 cluster-announce-ip就是该redis节点所在的主机ip,因为当前使用的是docker模拟的容器,所以此处写的应该是docker容器的ip。

另外我们还发现这里有俩端口号, 其中port就是redis节点自身绑定的端口(容器内的端口),不同的容器内部可以有相同的端口,后续进行端口映射时再把容器内的端口映射到容器外不同的端口号即可。

这里的第一个port也就是业务端口,bus-port则是管理端口。

一个服务器是可以绑定多个端口号的。

业务端口就是用来进行业务数据通信的,也就是响应客户端请求的。

管理端口则是为了完成一些管理上的任务来进行通信的,比如某个分片的redis主节点挂了,那么此时要让从节点成为主节点,就需要通过管理端口来完成对应的操作。 

关于配置的其它说明:
 

配置说明:
cluster-enabled yes 开启集群.
cluster-config-file nodes.conf 集群节点⽣成的配置.
cluster-node-timeout 5000 节点失联的超时时间.
cluster-announce-ip 172.30.0.101 节点⾃⾝ ip.
cluster-announce-port 6379 节点⾃⾝的业务端⼝.
cluster-announce-bus-port 16379 节点⾃⾝的总线端⼝. 集群管理的信息交互是通过这个端⼝进⾏的.

第二步:编写docker-compose.yml

version: '3.7'  
  
networks:  
  mynet:  
    ipam:  
      config:  
        - subnet: 172.30.0.0/24  
  
services:  
  redis1:  
    image: 'redis:5.0.9'  
    container_name: redis1  
    restart: always  
    volumes:  
      - ./redis1/:/etc/redis/  
    ports:  
      - "6371:6379"  
      - "16371:16379"  
    command: redis-server /etc/redis/redis.conf  
    networks:  
      mynet:  
        ipv4_address: 172.30.0.101  
  
  redis2:  
    image: 'redis:5.0.9'  
    container_name: redis2  
    restart: always  
    volumes:  
      - ./redis2/:/etc/redis/  
    ports:  
      - "6372:6379"  
      - "16372:16379"  
    command: redis-server /etc/redis/redis.conf  
    networks:  
      mynet:  
        ipv4_address: 172.30.0.102  
  
  redis3:  
    image: 'redis:5.0.9'  
    container_name: redis3  
    restart: always  
    volumes:  
      - ./redis3/:/etc/redis/  
    ports:  
      - "6373:6379"  
      - "16373:16379"  
    command: redis-server /etc/redis/redis.conf  
    networks:  
      mynet:  
        ipv4_address: 172.30.0.103  
  
  redis4:  
    image: 'redis:5.0.9'  
    container_name: redis4  
    restart: always  
    volumes:  
      - ./redis4/:/etc/redis/  
    ports:  
      - "6374:6379"  
      - "16374:16379"  
    command: redis-server /etc/redis/redis.conf  
    networks:  
      mynet:  
        ipv4_address: 172.30.0.104  
  
  redis5:  
    image: 'redis:5.0.9'  
    container_name: redis5  
    restart: always  
    volumes:  
      - ./redis5/:/etc/redis/  
    ports:  
      - "6375:6379"  
      - "16375:16379"  
    command: redis-server /etc/redis/redis.conf  
    networks:  
      mynet:  
        ipv4_address: 172.30.0.105  
  
  redis6:  
    image: 'redis:5.0.9'  
    container_name: redis6  
    restart: always  
    volumes:  
      - ./redis6/:/etc/redis/  
    ports:  
      - "6376:6379"  
      - "16376:16379"  
    command: redis-server /etc/redis/redis.conf  
    networks:  
      mynet:  
        ipv4_address: 172.30.0.106  
  
  redis7:  
    image: 'redis:5.0.9'  
    container_name: redis7  
    restart: always  
    volumes:  
      - ./redis7/:/etc/redis/  
    ports:  
      - "6377:6379"  
      - "16377:16379"  
    command: redis-server /etc/redis/redis.conf  
    networks:  
      mynet:  
        ipv4_address: 172.30.0.107  
  
  redis8:  
    image: 'redis:5.0.9'  
    container_name: redis8  
    restart: always  
    volumes:  
      - ./redis8/:/etc/redis/  
    ports:  
      - "6378:6379"  
      - "16378:16379"  
    command: redis-server /etc/redis/redis.conf  
    networks:  
      mynet:  
        ipv4_address: 172.30.0.108  
  
  redis9:  
    image: 'redis:5.0.9'  
    container_name: redis9  
    restart: always  
    volumes:  
      - ./redis9/:/etc/redis/  
    ports:  
      - "6379:6379"  
      - "16379:16379"  
    command: redis-server /etc/redis/redis.conf  
    networks:  
      mynet:  
        ipv4_address: 172.30.0.109  
  
  redis10:  
    image: 'redis:5.0.9'  
    container_name: redis10  
    restart: always  
    volumes:  
      - ./redis10/:/etc/redis/  
    ports:  
      - "6380:6379"  
      - "16380:16379"  
    command: redis-server /etc/redis/redis.conf  
    networks:  
      mynet:  
        ipv4_address: 172.30.0.110  
  
  redis11:  
    image: 'redis:5.0.9'  
    container_name: redis11  
    restart: always  
    volumes:  
      - ./redis11/:/etc/redis/  
    ports:  
      - "6381:6379"  
      - "16381:16379"  
    command: redis-server /etc/redis/redis.conf  
    networks:  
      mynet:  
        ipv4_address: 172.30.0.111

 关于redis的节点的配置很多都是重复的,不过要特别注意ip地址要与刚刚redis配置文件中的保持一致。

还有在开头的时候,先创建了一个network,并分配网段:172.30.0.0/24。

关于每个节点的端口映射不配置也可以,配置的目的是为了可以通过宿主机用ip + 映射端口进行访问,也可以通过容器自身ip:6379的方式访问。

 

 第三步:启动容器

执行命令:

docker-compose up -d

-d表示程序在后台运行。 

 补充一些操作:

停止现在运行的所有容器:

docker stop $(docker ps -q)

查看当前的容器

docker ps

如果之前的redis服务没有关闭,导致端口冲突了,那么启动就可能会是这个样子:

启动成功应该是这样的:

此时虽然启动成功,但是此时它们还并没有成为一个集群,而是各自管自己的。

第四步:构建集群 

执行命令(在命令行中):

redis-cli --cluster create 172.30.0.101:6379 172.30.0.102:6379 
172.30.0.103:6379 172.30.0.104:6379 172.30.0.105:6379 172.30.0.106:6379 
172.30.0.107:6379 172.30.0.108:6379 172.30.0.109:6379 --cluster-replicas 2

 其中:cluster create表示要建立一个集群,后面就要填写每个节点的ip和端口。

--cluster-replicas 2:表示每个主节点有两个从节点。这个配置设置了之后,redis也就是知道了一个分片有三个节点。

另外,redis在构建集群的时候,谁是主节点,谁是从节点,谁和谁是一个分片,这些都是不固定的。本身从集群的角度来看,提供的这些节点本身应该是等价的。

 输入命令后:

可以看到redis已经将哈希槽位给主节点们分配好了,这里需要再输入yes才开始真正构建集群。

成功了:

 另外,关于客户端这里:
 

此时, 使⽤客⼾端连上集群中的任何⼀个节点, 都相当于连上了整个集群.
客⼾端后⾯要加上 -c 选项, 否则如果 key 没有落到当前节点上, 是不能操作的. -c 会⾃动把请求
重定向到对应节点.
使⽤ cluster nodes 可以查看到整个集群的情况

cluster nodes 

 

 可以看到,我们set的操作会先对key进行一个hash运算,得出它在哪个哈希槽位,如果当前客户端不在这个槽位,那么就会将请求重定向到对应的主节点上,并且将我们的客户端的连接也切换到对应的节点上。如果启动客户端时不加 -c选项,那么就会报错,插入也就失败了。

我们通过cluster nodes 查看,比如看到主机号是101这个节点是一个从节点,我们登录上去

然后set了一个key2,发现这个命令也会被重定向到一个主节点上,跟之前的操作一样。 

在使用集群之后,我们之前学过的大部分命令都是可以正常使用的,但是有些操作多个key的指令就不一定了,如果这多个key最终映射到了不同的主节点上,那么就会报错。

演示主节点宕机 

如果是从节点挂了,那么问题不是很大。如果是主节点挂了,那么写操作就不能执行了。

此时集群所作的工作跟哨兵机制那里类似,都会挑选一个挂掉的主节点所属的从节点,让其成为主节点。

之前我们看到了redis1 (主机号101)就是一个主节点,我们可以把它停止掉。

docker stop redis1

停掉之后,随便连上一个节点,然后查看一下:

CLUSTER NODES

仔细观察,发现有一个节点就处于了一个fail状态。 

然后又发现,主机号105的节点原本是从节点,现在就变成了主节点了。

此时如果我们再重新启动redis1节点

docker start redis1

 

 

此时主机号101就变成了一个从节点重新加入到了这个分片中。

可以使⽤ cluster failover 进⾏集群恢复. 也就是把 101 重新设定成 master. (登录到 101 上执⾏)
cluster failover

集群的故障处理

 由此可见,集群也具有处理故障转移的功能。

1.先进行故障判定:

集群中的所有节点, 都会周期性的使⽤⼼跳包进⾏通信.

 

1. 节点 A 给 节点 B 发送 ping 包, B 就会给 A 返回⼀个 pong 包. ping 和 pong 除了 message type
属性之外, 其他部分都是⼀样的. 这⾥包含了集群的配置信息(该节点的id, 该节点从属于哪个分⽚,
是主节点还是从节点, 从属于谁, 持有哪些 slots 的位图...).

 

2. 每个节点, 每秒钟, 都会给⼀些随机的节点发起 ping 包, ⽽不是全发⼀遍. 这样设定是为了避免在节点很多的时候, ⼼跳包也⾮常多(⽐如有 9 个节点, 如果全发, 就是 9 * 8 有 72 组⼼跳了, ⽽且这是按照 N^2 这样的级别增⻓的).

 

3. 当节点 A 给节点 B 发起 ping 包, B 不能如期回应的时候, 此时 A 就会尝试重置和 B 的 tcp 连接, 看能否连接成功. 如果仍然连接失败, A 就会把 B 设为 PFAIL 状态(相当于主观下线).

 

4. A 判定 B 为 PFAIL 之后, 会通过 redis 内置的 Gossip 协议, 和其他节点进⾏沟通, 向其他节点确认 B的状态. (每个节点都会维护⼀个⾃⼰的 "下线列表", 由于视⻆不同, 每个节点的下线列表也不⼀定相同).

 

5. 此时 A 发现其他很多节点, 也认为 B 为 PFAIL, 并且数⽬超过总集群个数的⼀半, 那么 A 就会把 B 标记成 FAIL (相当于客观下线), 并且把这个消息同步给其他节点(其他节点收到之后, 也会把 B 标记成FAIL).

 

⾄此, B 就彻底被判定为故障节点了.

 

 某个节点宕机,有的时候会引起整个集群都宕机。(fail状态)

以下三种情况会导致集群宕机:

a.某个分片中,所有的主节点和从节点都挂了。

b.某个分片,主节点挂了,但是没有从节点,其实就跟a一样。

c.超过半数的主节点挂了。

 

 2.故障转移:

上述例⼦中, B 故障, 并且 A 把 B FAIL 的消息告知集群中的其他节点.
如果 B 是从节点, 那么不需要进⾏故障迁移.
如果 B 是主节点, 那么就会由 B 的从节点 (⽐如 C 和 D) 触发故障迁移了

 

所谓故障迁移, 就是指把从节点提拔成主节点, 继续给整个 redis 集群提供⽀持.
具体流程如下:
1. 从节点判定⾃⼰是否具有参选资格. 如果从节点和主节点已经太久没通信(此时认为从节点的数据和主节点差异太⼤了), 时间超过阈值, 就失去竞选资格.

 

2. 具有资格的节点, ⽐如 C 和 D, 就会先休眠⼀定时间. 休眠时间 = 500ms 基础时间 + [0, 500ms] 随机时间 + 排名 * 1000ms. offset 的值越⼤, 则排名越靠前(越⼩).

 

3. ⽐如 C 的休眠时间到了, C 就会给其他所有集群中的节点, 进⾏拉票操作. 但是只有主节点才有投票资格.

 

4. 主节点就会把⾃⼰的票投给 C (每个主节点只有 1 票). 当 C 收到的票数超过主节点数⽬的⼀半, C 就会晋升成主节点. (C ⾃⼰负责执⾏ slaveof no one, 并且让 D 执⾏ slaveof C).

 

5. 同时, C 还会把⾃⼰成为主节点的消息, 同步给其他集群的节点. ⼤家也都会更新⾃⼰保存的集群结构信息.

 

上述选举的过程, 称为 Raft 算法, 是⼀种在分布式系统中⼴泛使⽤的算法.
在随机休眠时间的加持下, 基本上就是谁先唤醒, 谁就能竞选成功。

 

集群的扩容 

扩容是⼀个在开发中⽐较常遇到的场景 

所谓分布式的本质, 就是使⽤更多的机器, 引⼊更多的硬件资源。

 第⼀步: 把新的主节点加⼊到集群:

上⾯已经把 redis1 - redis9 重新构成了集群. 接下来把 redis10 和 redis11 也加⼊集群.

这里把redis10作为主节点,redis11作为从节点。

redis-cli --cluster add-node 172.30.0.110:6379 172.30.0.101:6379

这里后面还写了一个主机号为101的节点,后面这个节点只要是集群里的节点都可以,目的就是为了表示我们要在这个集群中加入新的节点(分片)。 

 执行成功:

 第⼆步: 重新分配 slots:
 

redis-cli --cluster reshard 172.30.0.101:6379
reshard 后的地址是集群中的任意节点地址.
另外, 注意单词拼写, 是 reshard (重新切分), 不是 reshared (重新分享) , 不要多写个 e.

 

执行后: 

 

多少个 slots 要进⾏ reshard ? (此处我们填写 4096)
哪个节点来接收这些 slots ? (此处我们填写 172.30.0.110 这个节点的集群节点 id)
这些 slots 从哪些节点搬运过来? (此处我们填写 all, 表⽰从其他所有的节点都进⾏搬运)

 

这里的node ID是

 

 

在搬运 key 的过程中, 对于那些不需要搬运的 key, 访问的时候是没有任何问题的. 但是对于需
要搬运的 key, 进⾏访问可能会出现短暂的访问错误 (key 的位置出现了变化).
随着搬运完成, 这样的错误⾃然就恢复了

 

第三步: 给新的主节点添加从节点

光有主节点了, 此时扩容的⽬标已经初步达成. 但是为了保证集群可⽤性, 还需要给这个新的主节点添加从节点, 保证该主节点宕机之后, 有从节点能够顶上。

 

redis-cli --cluster add-node 172.30.0.111:6379 172.30.0.101:6379 --cluster-slave --cluster-master-id [172.30.1.110 节点的 nodeId]

 

其中这个主机号101的只是为了表示这个集群,写集群中的哪个节点都无所谓。

 

 

关于集群的缩容(了解) 

扩容是⽐较常⻅的, 但是缩容其实⾮常少⻅. 此处我们简单了解缩容的操作步骤即可.
接下来演⽰把 110 111 这两个节点删除

 

删除从节点:
此处删除的节点 nodeId 是
111 节点的 id

redis-cli --cluster del-node [集群中任⼀节点ip:port] [要删除的从机节点 nodeId]

 

 

;