0


[Redis#19] 集群 | 数据分片 | docker模拟 | 故障转移 | 集群扩容

  • 本章节相关操作不需要记忆,后续工作中如果用到了能查到即可。
  • 重点在于理解流程和原理。
基本概念
  • 上篇文章讲述的哨兵模式, 提高了系统的可用性. 但是真正用来存储数据的还是 master 和 slave 节点. 所有的数据都需要存储在单个 master 和 slave 节点中.
  • 如果数据量很大, 接近超出了 master/slave 所在机器的物理内存, 就可能出现严重问题了.
  • 虽然硬件价格在不断降低, 一些中大厂的服务器内存已经可以达到 TB 级别了, 但是 1TB 在当前这个 "大数据" 时代, 俨然不算什么, 有的时候我们确实需要更大的内存空间来保存更多的数据.

如何获取更大的空间?

加机器即可! 所谓 "大数据" 的核心, 其实就是一台机器搞不定了, 用多台机器来搞.

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

集群

集群这个词,可以从广义上来理解,也可以从狭义上来理解。

  • 其中广义的集群,只要是多个机器,构成了分布式系统。我们前面学过的哨兵模式,也可以算作是一种广义上的集群。
  • 狭义的集群,是 Redis 提供的一种集群模式,在这个集群模式之下,主要解决存储空间不足的问题,即 拓展存储空间。

我们前面的哨兵模式提高了系统的可用性,哨兵模式中,本质上还是 Redis 主从节点存储数据,其中要是请求一个主节点/从节点,就得存储整个数据的“全集”。

  • 但是集群中主要要解决的问题就是要 引入多台机器, 每台机器存储一部分数据。
  • 只要机器规模足够大, 就可以存储任意数据的大小了。比如:

  • Redis 的集群通过引入多组 Master/Slave 来分散数据存储压力。
  • 每一组 Master/Slave 存储数据全集的一部分,构成一个更大的整体。
  • 假定整个数据 全集为 1TB,三组 Master/Slave 可以每组存储 1/3 的数据。
  • 每个 Slave 是对应 Master 的备份(Master 故障时,Slave 会补位成 Master)。
  • 每个 红框部分称为一个分片 (Sharding),用于进一步扩展存储容量。

关于使用硬盘代替内存

  • 硬盘虽然可以存储更多数据,但访问速度远慢于内存。
  • 在某些应用场景(如搜索引擎),既需要存储较多数据,又希望有非常高的读写速度。这时我们就可以引入 redis 集群啦

这其中每个服务器集群上都需要存储一定规模的数据,也就是把数据分为了多份,但是数据在分成多份的时候,应该怎么分?这就是我们接下来需要主要研究的问题。


数据分片算法

分片的核心思路是用多组机器来存数据的每个部分,那么接下来的核心问题就是,给定一个数据(一个具体的 key),那么这个数据应该存储在哪个分片上?读取的时候又应该去哪个分片读取?围绕这个问题,业界有三种比较主流的实现方式:

  1. 哈希求余
  2. 一致性哈希算法
  3. 哈希槽分区算法

1. 哈希求余

哈希求余,就是借鉴了哈希表的基本思想

借助哈希函数,把一个 key 映射到整数,再针对数据的长度,求余就可以得到一个数组的下标。

哈希求余的分片算法具体是什么样子的呢?

  • 比如有三个分片,编号 0 1 2,此时就可以针对要插入的数据的 key 计算出一个哈希值
  • (计算 哈希值的算法,一般是针对一个字符串里面的内容进行一系列的变换,比如** md5 **算法就是一种哈希算法,它是把一个字符串变换成为一个 16 进制的数字)。
  • 在把这个 哈希值余上一个分片个数,就会得到一个下标。此时就可以把这个数据放到该下标对应的分片中了。

  • 比如,hash(key) % N => 0,由于求出的余数是 0,此时这个 key 对应的数据就要 存储在 0 号分片中。
  • 后续查询 key 的时候也是同样的算法。只要 key 是一样的,hash 函数式一样的,得到的分片的值就是一样的。
  • 随着业务逐渐增长,数据变多的时候,仅有的几个分片就不足以保存数据了,这就需要对集群进行扩容,引入新的分片。

**这时候,哈希求余算法的缺点就体现出来了,就是在增加集群容量,需要对数据进行 **搬运的时候,开销非常大,很多数据都需要搬运。原因就是,扩容后

  1. N

的值会变大,那么原先的求余就不再适用了,此时要把 所有数重新计算。计算完毕后,还要面临复杂的搬运数据的过程。,大部分的数据都是需要搬运的,这就是扩容的开销非常大。

从上图中可以看到,其中的 21 个数据,只有 3 个 key 没有被搬运,其他的 key 都是搬运过的。

当然,哈希求余的分片算法也是有一定优点的,那就是 简单高效,数据分配比较均匀。


2 一致性哈希算法

在哈希求余这种操作中,当前的 key 属于哪个分片是交替的。例如某些数据的 hash 值分别是 102, 103, 104,这些数据在对 3 求余后,得到的值分别是 0, 1, 2,它们哈希求余求出的值是交替的,交替分布在不同的集群中。

而在一致性哈希算法中,我们改进了这种交替存储的方式为连续存储。一致性哈希算法的过程如下:

  • 数据空间映射:首先把数据空间全部映射到一个圆环上,数据按照顺时针方向增长。
  • 分片定位:假设当前存在三个分片,将这些 分片放到圆环的某个位置。
  • Key 映射与分配:对于每一个 key,通过哈希函数 计算得到 哈希值 H,并将 H 映射到圆环上的对应位置。从 H 所在的位置开始,顺时针向下找,找到的第一个分片即为该 key 所从属的分片。

这相当于 N 个分片的位置将整个圆环分成了 N 个管辖区间,key 的哈希值落在某个区间内,就归该区间管理。

在这种一致性哈希算法情况下,如果需要对数据进行扩容,原有分片在环上的位置不动,只需要在环上 安排一个新的分片(如下图粉色区域)。

此时只需把 3 号分片上的部分数据搬运给新增的分片(如 4 号分片),而其他分片管理的区间保持不变。

这种方式 相比哈希求余减少了数据搬运成本,但可能导致各分片的数据量不均匀,出现 数据倾斜 现象。


3. 哈希槽分区算法 (Redis 使用)

  • 求余%:扩容不便
  • 一致性分片:数据倾斜

为了解决上述问题,Redis 集群引入了哈希槽算法,其公式为:

  1. hash_slot = crc16(key) % 16384
  • 这里 CRC 是一种哈希算法,16384 等于 16*1024 或者说是 2^14。
  • 此算法 结合了 一致性哈希 和 哈希求余 的特点
  • 将哈希值映射到 16384 个槽位上,然后把这些 槽位均匀地分配给每个分片。每个分片会使用“位图” 这样的数据结构来记录自己持有的槽位号,用 0/1 表示是否持有该槽位。

假设当前有三个分片,一种可能的分配方式如下:

  • 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 个槽位

此时,每个分片分出一部分槽位给新来的分片,并且保证最终所有分片持有的槽位个数接近,此处因为 **

  1. 16384 / 4

**可以除尽,所以最终四个分片的槽位数目完全相同。

最后进行数据拷贝时,只需要

  1. 0 1 2

三个分片,分别单向拷贝一部分数据给

  1. 3

号分片即可。

此外,还有一些关于 Redis 集群的问题需要注意:

Redis 集群最多有 16384 个分片吗?

在 Redis 集群中,key 是要先映射到槽位上,再映射到分片上的。这种设计的目的是为了确保数据分布的均匀性:

  • 如果每个分片包含的槽位较多,并且槽位个数相当,那么可以认为每个分片包含的 key 数量也是相等的,从而保证了数据分布的均匀性。
  • 然而,如果每个分片包含的槽位非常少,槽位的数量可能无法直观地反映出 key 的数量,这种情况之下,数据的均匀性就难以保证。
  • 此外,如果每个分片中包含的槽数非常少,则会导致集群中的服务器规模变得非常庞大,随着服务器数量的增加,出现故障的概率也会相应增大。

为什么是

  1. 16384

个槽位?

  • 节点之间通过心跳包通信,心跳包中包含了该节点持有哪些槽位,这个是使用位图这样的数据结构表示的。
  • 表示 16384 个槽位需要的位图大小是 2KB,如果给定的槽位数更多了,此时就需要消耗更多的空间。
  • 这对于内存来说不算什么,但是 在频繁的网络心跳包中,还是一个不小的开销,因为网络带宽是比内存更稀缺的资源
  • 另一方面,Redis 集群一般不建议超过 1000 个分片。16384个槽位,对于最大 1000 个分片来说是足够用的,同时也会使对应的槽位配置位图体积不至于很大

参考:


Docker搭建集群

接下来使用

  1. docker

搭建一个如下集群,拓扑结构如下:

注意

  • 本示例中我们将创建11个Redis节点
  • 其中前9个用于演示集群的搭建,后两个用于演示集群扩容。

i:创建目录和配置文件

首先,创建一个名为

  1. redis-cluster

的目录,并在该目录下创建两个文件:

编写
  1. generate.sh

脚本

  1. generate.sh

内容如下,此脚本将为每个 Redis 实例创建配置文件并设置不同的 IP 地址:

  1. #!/bin/bash
  2. # 创建并配置前9个节点
  3. for port in $(seq 1 9); do
  4. mkdir -p redis${port}
  5. touch redis${port}/redis.conf
  6. cat << EOF > redis${port}/redis.conf
  7. port 6379
  8. bind 0.0.0.0
  9. protected-mode no
  10. appendonly yes
  11. cluster-enabled yes
  12. cluster-config-file nodes.conf
  13. cluster-node-timeout 5000
  14. cluster-announce-ip 172.30.0.10${port}
  15. cluster-announce-port 6379
  16. cluster-announce-bus-port 16379
  17. EOF
  18. done
  19. # 创建并配置第10和11个节点(用于后续扩容)
  20. for port in $(seq 10 11); do
  21. mkdir -p redis${port}
  22. touch redis${port}/redis.conf
  23. cat << EOF > redis${port}/redis.conf
  24. port 6379
  25. bind 0.0.0.0
  26. protected-mode no
  27. appendonly yes
  28. cluster-enabled yes
  29. cluster-config-file nodes.conf
  30. cluster-node-timeout 5000
  31. cluster-announce-ip 172.30.0.1${port}
  32. cluster-announce-port 6379
  33. cluster-announce-bus-port 16379
  34. EOF
  35. done

C++ 和 Java 中的循环

  • C++: range based for(C++ 也有 std::for_each,STL 中的一个函数)
  • Java: for each

Shell 脚本中的循环如上

解释

  • **seq**: 一个 Linux 命令,生成 [1, 9]

  • **\**: 续行符,把下一行的内容和当前行合并成一行。

    • 默认情况下,shell 要求把所有的代码都写到一行里,使用续行符来换行。
  • **{ }**: 在 shell 中表示变量,不是表示代码块。

    • 对于 for,使用 dodone 来表示代码块的开始和结束。- 循环体内的代码。
  • 字符串拼接:在 shell 中拼接字符串是直接写到一起,而不需要使用 +

预期效果

  • 得到 11 个目录,每个目录里都有一个配置文件。
  • 配置文件中,IP 地址各不相同。

注意:**

  1. cluster-announce-ip 172.30.0.10${port}

**的值对于每个实例是不同的,以确保它们在网络中的唯一性。


执行生成命令

运行以下命令以执行

  1. generate.sh

脚本,这将在

  1. redis-cluster/

目录下生成相应的子目录及配置文件:

  1. bash generate.sh

生成后的目录结构应类似于:

每个

  1. redis.conf

文件的内容除了

  1. cluster-announce-ip

不同外,其他部分都相同。例如,

  1. redis1/redis.conf

包含如下内容:

配置说明

  • 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: 节点自身的总线端口,用于集群管理信息交互。

注意:

  • cluster-announce-ip: Docker 容器的 IP 地址。
  • port: 容器内的 Redis 端口。
  • 业务端口: 用于业务数据通信。(eg. 客户端访问的
  • 管理端口: 用于管理任务通信。

故障处理

  • 如果某个分片中的 Redis 主节点挂了,就需要让从节点成为主节点,就需要通过刚才 管理端口 来完成相应的操作。
  • 服务器端口绑定: 一个服务器可以绑定多个端口号。

ii:编写

  1. docker-compose.yml

接下来,我们需要编写

  1. docker-compose.yml

文件来定义 Docker 容器网络和服务。以下是配置示例:

  1. networks:
  2. mynet:
  3. ipam:
  4. config:
  5. - subnet: 172.30.0.0/24
  6. services:
  7. redis1:
  8. image: 'redis:5.0.9'
  9. container_name: redis1
  10. restart: always
  11. volumes:
  12. - ./redis1/:/etc/redis/
  13. ports:
  14. - 6371:6379
  15. - 16371:16379
  16. command:
  17. redis-server /etc/redis/redis.conf
  18. networks:
  19. mynet:
  20. ipv4_address: 172.30.0.101
  21. redis2:
  22. image: 'redis:5.0.9'
  23. container_name: redis2
  24. restart: always
  25. volumes:
  26. - ./redis2/:/etc/redis/
  27. ports:
  28. - 6372:6379
  29. - 16372:16379
  30. command:
  31. redis-server /etc/redis/redis.conf
  32. networks:
  33. mynet:
  34. ipv4_address: 172.30.0.102
  35. redis3:
  36. image: 'redis:5.0.9'
  37. container_name: redis3
  38. restart: always
  39. volumes:
  40. - ./redis3/:/etc/redis/
  41. ports:
  42. - 6373:6379
  43. - 16373:16379
  44. command:
  45. redis-server /etc/redis/redis.conf
  46. networks:
  47. mynet:
  48. ipv4_address: 172.30.0.103
  49. redis4:
  50. image: 'redis:5.0.9'
  51. container_name: redis4
  52. restart: always
  53. volumes:
  54. - ./redis4/:/etc/redis/
  55. ports:
  56. - 6374:6379
  57. - 16374:16379
  58. command:
  59. redis-server /etc/redis/redis.conf
  60. networks:
  61. mynet:
  62. ipv4_address: 172.30.0.104
  63. redis5:
  64. image: 'redis:5.0.9'
  65. container_name: redis5
  66. restart: always
  67. volumes:
  68. - ./redis5/:/etc/redis/
  69. ports:
  70. - 6375:6379
  71. - 16375:16379
  72. command:
  73. redis-server /etc/redis/redis.conf
  74. networks:
  75. mynet:
  76. ipv4_address: 172.30.0.105
  77. redis6:
  78. image: 'redis:5.0.9'
  79. container_name: redis6
  80. restart: always
  81. volumes:
  82. - ./redis6/:/etc/redis/
  83. ports:
  84. - 6376:6379
  85. - 16376:16379
  86. command:
  87. redis-server /etc/redis/redis.conf
  88. networks:
  89. mynet:
  90. ipv4_address: 172.30.0.106
  91. redis7:
  92. image: 'redis:5.0.9'
  93. container_name: redis7
  94. restart: always
  95. volumes:
  96. - ./redis7/:/etc/redis/
  97. ports:
  98. - 6377:6379
  99. - 16377:16379
  100. command:
  101. redis-server /etc/redis/redis.conf
  102. networks:
  103. mynet:
  104. ipv4_address: 172.30.0.107
  105. redis8:
  106. image: 'redis:5.0.9'
  107. container_name: redis8
  108. restart: always
  109. volumes:
  110. - ./redis8/:/etc/redis/
  111. ports:
  112. - 6378:6379
  113. - 16378:16379
  114. command:
  115. redis-server /etc/redis/redis.conf
  116. networks:
  117. mynet:
  118. ipv4_address: 172.30.0.108
  119. redis9:
  120. image: 'redis:5.0.9'
  121. container_name: redis9
  122. restart: always
  123. volumes:
  124. - ./redis9/:/etc/redis/
  125. ports:
  126. - 6379:6379
  127. - 16379:16379
  128. command:
  129. redis-server /etc/redis/redis.conf
  130. networks:
  131. mynet:
  132. ipv4_address: 172.30.0.109
  133. redis10:
  134. image: 'redis:5.0.9'
  135. container_name: redis10
  136. restart: always
  137. volumes:
  138. - ./redis10/:/etc/redis/
  139. ports:
  140. - 6380:6379
  141. - 16380:16379
  142. command:
  143. redis-server /etc/redis/redis.conf
  144. networks:
  145. mynet:
  146. ipv4_address: 172.30.0.110
  147. redis11:
  148. image: 'redis:5.0.9'
  149. container_name: redis11
  150. restart: always
  151. volumes:
  152. - ./redis11/:/etc/redis/
  153. ports:
  154. - 6381:6379
  155. - 16381:16379
  156. command:
  157. redis-server /etc/redis/redis.conf
  158. networks:
  159. mynet:
  160. ipv4_address: 172.30.0.111
网络配置
  • subnet: 此处为了后续创建静态的 IP,就需要先手动创建出网络,同时给这个网段分配 IP。这里分配的 网络号是 172.30.0,其中这个网络号是私网格式的一种,需要注意的是,不能和当前主机上现有的其他网段冲突,0/24 是主机号。

  • volumes: 配置文件映射,确保容器内的配置文件与宿主机上的配置文件同步更新。

  • ports: 端口映射,需要注意的是,映射右边的端口必须和配置文件中的保持一致。

  • ipv4_address: 静态配置的网络 IP,这里的 IP 也必须和之前的配置文件中的一致。

启动容器之前,要把之前测试的容器关掉,避免冲突

博主在测试机上没什么有用的,就粗暴的直接全都删掉了

由于要启动是一个容器,所以上面的文件会比较长。

启动容器:

  1. docker compose up -d

此时十一个容器就启动了。

但是这些容器目前还是单独的节点,尚未构成集群。


iii: 构建集群

接下来需要把这些服务容器全部构建为集群,使用如下命令:

  1. 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 就会知道,每 3 个节点是一组(一个分片)。但是分配的时候,谁是主节点,谁是从节点,都是随机的。

这样

  1. redis

就会依据规则自动构建集群,每三个节点构成一个分片(因为指定了一个主有两个从),并且会自动构建主从关系。

输入命令后,出现以下界面:

  • 1 部分,是信号槽的分配部分,可以看到redis内部采用了连续分配的方式,将信号槽均匀的分配给三个分片。
  • 2 部分,是主从关系的建立部分,第一行表示:172.30.0.105:6379成为172.30.0.101:6379的从节点。那么这六行日志,就表示xxx.101xxx.102xxx.103成为了三个主节点,其余的节点成为这三个的从节点。
  • 3 部分,是一段日志,就是具体把那一部分槽位具体分配给哪一个分片,比如xxx.101拿到了[0-5460]的槽位号。

输入

  1. yes

之后才会真正创建。

见到

  1. [OK]

之后说明集群建立完成。

连接并验证集群

使用客户端连接集群中的任何一个节点,都相当于连上了整个集群。:

  1. redis-cli -h 172.30.0.101 -p 6379

在客户端中,我们使用

  1. cluster nodes

来查看当前集群的信息。

重定向

尝试插入数据:

报错了,因为当前集群发生了分片,每个分片都只能存储一部分数据,

  1. key1

经过哈希运算,发现并不是这个节点可以存储的值,于是就报错了。

想要解决这个问题,可以在启动时加上

  1. -c

选项,此时插入数据,会自动重定向到对应的节点。

如图,每次操作数据是,如果该数据不属于当前分片,就会触发一次重定向,自动跳转到对应的客户端。命令行前面的 ip 一直在改变,这就说明我们的客户端一直在切换。

但是

  1. redis

中,有一些命令同时操作多个

  1. key

,比如最后一个命令

  1. mget

,此时又报错了。因为这几个

  1. key

属于不同分片,那么就无法同时处理,因此在集群的情况下,最好不要一次性操作多个

  1. key


主节点宕机自动处理流程

如果在集群中,某一个分片的主节点宕机了,会发生什么?在部署集群时,并没有引入哨兵节点,但是集群也会完成哨兵的工作,如果主节点宕机了,集群会自动完成重新选主的过程。

接下来就讲解集群中是如何完成故障转移的。

故障判定

Redis 集群中的所有节点会周期性地使用心跳包进行通信,确保各节点之间的连接状态和集群配置信息的同步。以下是心跳包交互的具体过程:

  • 心****跳包通信:节点A给节点B发送ping包,B接收到后返回一个pong包。这些消息除了类型属性外,其余部分相同,并包含以下集群配置信息:

    • 节点ID- 所属分片- 主/从角色- 附属主节点(如果为从节点)- 持有的哈希槽位图
  • 随机化心跳检测:每个节点每秒钟随机选择一部分节点发送ping包,而非向所有节点发送,以减少大量节点存在时的心跳包数量。

  • PFAIL 状态:当节点A尝试与节点B通信未果时,A首先尝试重置TCP连接;若仍然失败,则将B标记为PFAIL(主观下线)状态。

  • Gossip协议确认:一旦A判定B为PFAIL,它会通过内置的Gossip协议与其他节点沟通,确认B的状态。每个节点维护自己的“下线列表”,反映其视角下的故障节点。

  • FAIL 状态:如果多数节点(超过半数)也认为B处于PFAIL状态,那么A将正式把B标记为FAIL(客观下线),并通知其他节点更新状态。

故障迁移

当一个节点被标记为

  1. FAIL

时,根据该节点的角色,可能触发不同的响应:

  • 从节点故障:如果是从节点,通常不需要特别处理,因为它们不直接服务客户端请求。
  • 主节点故障:如果故障节点是主节点,则需要进行故障迁移,流程如下:
    1. 资格审查:从节点检查自身是否符合晋升条件,即与原主节点的最后通信时间不超过设定阈值。2. 休眠等待:符合条件的从节点进入一段随机的休眠期,**休眠时间=500ms基础时间+[0,500ms]随机时间+排名*1000ms**offset 的值越大,则排名越靠前(越小)。这一策略有助于防止多个从节点同时竞选。3. 拉票选举:休眠结束后的 从节点开始向其他集群的主节点拉票。只有主节点有投票权,且每个主节点只能投一票。4. 晋升为主:获得超过半数主节点支持的从节点晋升为主节点,并执行SLAVEOF NO ONE命令脱离原主节点控制。并且让同一分片中的其它节点执行 slaveof5. 最后,新的主节点会把自己成为主节点的消息,同步给其他集群的节点,大家也都会更新自己保存的集群结构信息

此过程类似于 Raft算法,旨在选出网络状况较好的节点作为新的主节点,保证集群的服务连续性和数据一致性。

Fail 状态的影响

在某些情况下,单个节点的故障可能导致整个集群进入

  1. fail

状态,具体情形包括但不限于:

  • 某一分片内的所有主节点和从节点都不可用。
  • 某一分片内主节点故障且 无可用从节点可晋升。
  • 超过半数的主节点失效。

实验

如图:

首先通过

  1. docker stop redis1

,关掉了

  1. redis1

节点,也就是

  1. xxx.101

下线了,而这是一个主节点。登录

  1. 6372

端口的客户端,查看当前集群,可以发现

  1. xxx.106

成为了新的主节点,而

  1. xxx.105

原先是

  1. xxx.101

的从节点。

重启

  1. redis1

,其变为了

  1. reids5

的从节点。

此处 集群的故障转移,和哨兵的故障转移是有一些差别的

  • 哨兵:sdown/odown
  • 集群:PFAIL/FAIL

集群扩容

一、加入集群
  • 命令格式

    • 使用 --cluster add-node 选项将新节点添加到集群。
  • 具体操作

    • 新增节点:要增加到集群的节点。- 集群任意节点:用于标识要加入哪一个集群。
  1. redis-cli --cluster add-node 172.30.0.110:6379 172.30.0.101:6379

这样就可以把

  1. xxx.110

节点加入到集群中,登入任意客户端查看:

通过上述命令,可以将

  1. xxx.110

节点加入到集群中。登录任意客户端查看,可以看到集群内部已有

  1. xxx.110

,且是一个主节点。但需要注意的是,新增节点没有分配哈希槽。

二、分配哈希槽
  • 命令格式

    • 使用--cluster reshard 选项给新节点分配哈希槽(注意是 reshard 不是 shared)。
  1. redis-cli --cluster reshard 172.30.0.101:6379

执行步骤

  • 系统会询问要移动多少个 slots(哈希槽)。假设需要移动4096个哈希槽给新节点,则输入 4096
  • 接着询问将这些哈希槽移动给哪个节点。此时应选择 哈希槽为 0的主节点,并复制其ID。

  • 最后询问从哪些节点中空出这些哈希槽。可以 选择 all 表示从所有现有节点平均提取,或自己指定节点ID,以 done 结束。
  • 最终向用户确认是否要执行该操作。

执行完毕后,进入任意客户端查看集群现状,可以看到新节点获得了三个范围的哈希槽。

问题:搬运哈希槽的过程可能比较久,在此期间用户的访问合法性如何?

  • 大部分哈希槽在搬运过程中仍可正常访问。
  • 如果 用户访问正在移动的哈希槽,则可能会导致访问失败。
三、添加从节点
  • 命令格式

    • 使用 add-node 命令配合 --cluster-slave--cluster-master-id 选项来添加从节点。
  1. redis-cli --cluster add-node 新节点 --cluster-slave --cluster-master-id 主节点的ID
  • 参数说明

    • --cluster-slave:指定新添加的节点作为从节点。- --cluster-master-id:后面跟着的是主节点的ID,表示该节点从属于哪一个节点。
  1. redis-cli --cluster add-node 172.30.0.111:6379 172.30.0.101:6379 --clusterslave --cluster-master-id [172.30.1.110 节点的 nodeId-->] 7ec6d4a0e616916afab017f30d9a1434e375e80a

此时扩容目标初步达成,但是为了保证整个集群的可用性,还需要给主节点添加一些从结点,保证主节点宕机之后,有接班人~

sum:

主从复制:分担了读取压力

哨兵:监控,稳健性提高

集群:存储数据量 提升


本文转载自: https://blog.csdn.net/2301_80171004/article/details/144326900
版权归原作者 lvy- 所有, 如有侵权,请联系我们删除。

“[Redis#19] 集群 | 数据分片 | docker模拟 | 故障转移 | 集群扩容”的评论:

还没有评论