目录
1.集群搭建(基于 Docker)
-
拓扑结构如下 :
-
创建目录和配置 :创建
redis-cluster
⽬录,内部创建两个⽂件plaintextredis-cluster/ ├── docker-compose.yml └── generate.sh
-
generate.sh
:plaintextfor 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
- 配置说明 :
cluster-enabled yes
:开启集群cluster-config-file nodes.conf
:集群节点⽣成的配置cluster-node-timeout 5000
:节点失联的超时时间cluster-announce-ip 172.30.0.101
:节点⾃⾝ipcluster-announce-port 6379
:节点⾃⾝的业务端⼝cluster-announce-bus-port 16379
:节点⾃⾝的总线端⼝(管理端口),集群管理的信息交互是通过这个端⼝进⾏的
- 配置说明 :
-
docker-compose.yml
:-
先创建networks,并分配网段
172.30.0.0/24
-
配置每个节点,注意配置⽂件映射,端⼝映射,以及容器的ip地址,设定成固定ip⽅便后续的观察和操作
plaintextversion: '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: - ./redis1/:/etc/redis/ ports: - 6372:6379 - 16372:16379 command: redis-server /etc/redis/redis.conf networks: mynet: ipv4_address: 172.30.0.102 # redis3 ~ redis11
-
-
启动容器 :
docker-compose up -d
-
构建集群:此处把前9个主机构成集群,3主6从,后两个主机暂时不用
-
--cluster create
:表⽰建⽴集群.后⾯填写每个节点的ip和地址 -
--cluster-replicas 2
:表⽰每个主节点需要两个从节点备份plaintextredis-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
-
日志中会描述哪些是主节点,哪些从节点跟随哪个主节点
plaintext>>> Performing hash slots allocation on 9 nodes... Master[0] -> Slots 0 - 5460 Master[1] -> Slots 5461 - 10922 Master[2] -> Slots 10923 - 16383 Adding replica 172.30.0.105:6379 to 172.30.0.101:6379 Adding replica 172.30.0.106:6379 to 172.30.0.101:6379 Adding replica 172.30.0.107:6379 to 172.30.0.102:6379 Adding replica 172.30.0.108:6379 to 172.30.0.102:6379 Adding replica 172.30.0.109:6379 to 172.30.0.103:6379 Adding replica 172.30.0.104:6379 to 172.30.0.103:6379 M: e4f37f8f0ea0dafc584349999795716613910e51 172.30.0.101:6379 slots:[0-5460] (5461 slots) master M: 5f71983ad52cc7077ce8874ae1c4f9c23d9f502c 172.30.0.102:6379 slots:[5461-10922] (5462 slots) master M: b3c0a96f6a206088ecea639147b6fcf903afe872 172.30.0.103:6379 slots:[10923-16383] (5461 slots) master S: 85025819223f12615046c54d89f510e9cd0444a1 172.30.0.104:6379 replicates b3c0a96f6a206088ecea639147b6fcf903afe872 S: 2e5dc211288784ba55d554a377b87bfe2b5398db 172.30.0.105:6379 replicates e4f37f8f0ea0dafc584349999795716613910e51 S: 29f05d98982bd3df05d0222091e4b8ef9569f424 172.30.0.106:6379 replicates e4f37f8f0ea0dafc584349999795716613910e51 S: 3584840ac704c3ee016f3bdcca3f7ebe6f6e8e80 172.30.0.107:6379 replicates 5f71983ad52cc7077ce8874ae1c4f9c23d9f502c S: 0a889103b35db2a6e82e8c09904bbef310cff3b1 172.30.0.108:6379 replicates 5f71983ad52cc7077ce8874ae1c4f9c23d9f502c S: 00ba82bed6abeb015116d51d1af7fcb1609d03ad 172.30.0.109:6379 replicates b3c0a96f6a206088ecea639147b6fcf903afe872 Can I set the above configuration? (type 'yes' to accept): yes
-
Redis在构建集群时,谁是主节点,谁是从节点,谁和谁是一个分片,都是不固定的
- 理论上说,从集群角度看,提供的所有节点本就应该是等价的
-
-
此时,使用客户端连上集群中的任何一个节点,都相当于连上了整个集群
- 客⼾端后⾯要加上
-c
选项,否则如果key
没有落到当前节点上,是不能操作的-c
会自动把请求重定向到对应节点
- 使⽤
cluster nodes
可以查看到整个集群的情况
- 客⼾端后⾯要加上
-
使用集群之后,之前的那些Redis命令,基本都能使用,除了一些操作多个
key
的 ,如mget
![[Pasted image 20240911210424.png]]
2.主节点宕机
1.宕机后会发生什么?
- 此处集群做的工作,和之前哨兵做的类似,会主动从该主节点旗下从节点中挑一个出来,提拔为主节点
master
提示fail
后,原本的slave
会变成新的master
- 如果重新启动原
master
,该节点会作为slave
存在
- 可以使⽤
cluster failover
进⾏集群恢复,也就是把原master
重新设定成·master- 需要登录到原
master
机器上执行
- 需要登录到原
2.处理流程
1.故障判定
- 集群中的所有节点,都会周期性的使⽤⼼跳包进⾏通信 :
- 节点A给节点B发送
ping
包,B就会给A返回⼀个pong
包ping
和pong
除了message type
属性之外,其他部分都是⼀样的- 这⾥包含了集群的配置信息(该节点的id,该节点从属于哪个分⽚,是主节点还是从节点,从属于谁,持有哪些
slots
的位图...)
- 每个节点,每秒钟,都会给⼀些随机的节点发起
ping
包 ,⽽不是全发⼀遍- 这样设定是为了避免在节点很多的时候,⼼跳包也⾮常多
- ⽐如有9个节点,如果全发,就是 9 ∗ 8 9*8 9∗8有72组⼼跳了,⽽且这是按照 N 2 N^2 N2这样的级别增⻓的
- 当节点A给节点B发起
ping
包,B不能如期回应的时候,此时A就会尝试重置和B的TCP连接,看能否连接成功,如果仍然连接失败,A就会把B设为PFAIL
状态(相当于主观下线). - A判定B为
PFAIL
之后,会通过Redis内置的Gossip协议,和其他节点进⾏沟通,向其他节点确认B的状态- 每个节点都会维护⼀个⾃⼰的"下线列表",由于视⻆不同,每个节点的下线列表也不⼀定相同
- 此时A发现其他很多节点,也认为B为
PFAIL
,并且数⽬超过总集群个数的⼀半 ,那么A就会把B标记成FALL
(相当于客观下线 ),并且把这个消息同步给其他节点(其他节点收到之后,也会把B标记成FAIL
) - 至此,B是否下线,就已经明确了
- 节点A给节点B发送
- 某个或者某些节点宕机,有的时候会引起整个集群都宕机,称为
fail
状态- 某个分⽚,所有的主节点和从节点都挂了
- 此时,该分片就无法提供数据服务了,相当于数据缺失
- 某个分⽚,主节点挂了,但是没有从节点
- 超过半数的
master
节点都挂了- 如果突然一系列的
master
都挂了,说明集群遇到了非常严重的问题,此时就需要赶紧停下来,检查检查是不是有什么问题
- 如果突然一系列的
- 核心原则:保证每个
slots
都能正常工作(存取数据)
- 某个分⽚,所有的主节点和从节点都挂了
2.故障迁移
- 上述例⼦中,B故障,并且A把B
FAIL
的消息告知集群中的其他节点- 如果B是从节点,那么不需要进行故障迁移
- 如果B是主节点,那么就会由B的从节点(比如C和D)触发故障转移了
- 所谓故障转移,就是指把从节点提拔成主节点,继续给整个Redis集群提供支持
- 具体流程 :
- 从节点判定⾃⼰是否具有参选资格,如果从节点和主节点已经太久没通信(此时认为从节点的数据和主节点差异太⼤了),时间超过阈值,就失去竞选资格
- 具有资格的节点 ,⽐如C和D,就会先休眠⼀定时间 ,
休眠时间 = 500ms基础时间 + [0, 500ms]随机时间 + 排名 * 1000ms
,offset
的值越⼤,则排名越靠前(越⼩)- 排名越靠前,休眠时间就越短,谁休眠时间短,大概率就是新的主节点了
- 比如C的休眠时间到了,C就会给其他所有集群中的节点,进行拉票操作
- 但是只有主节点才有投票资格
- 主节点就会把⾃⼰的票投给C (每个主节点只有1票),当C收到的票数超过主节点数⽬的⼀半,C就会晋升成主节点
- C⾃⼰负责执⾏
slaveof no one
,并且让D执⾏slaveof C
- C⾃⼰负责执⾏
- 同时,C还会把⾃⼰成为主节点的消息,同步给其他集群的节点,⼤家也都会更新⾃⼰保存的集群结构信息
- 上述选举的过程,成为
Raft
算法,是一种在分布式系统中广泛使用的算法- 在随即休眠时间的加持下,基本上就是谁先醒,谁就能竞选成功
- 更多的时候,是为了选一个节点出来,至于是谁,并不重要
- 哨兵是先选出
leader
,leader
负责找一个从节点升级成主节点,这里是直接投票选出新的主节点
3.集群扩容
0.前言
- 扩容是⼀个在开发中⽐较常遇到的场景
- 随着业务的发展,现有集群很可能⽆法容纳⽇益增⻓的数据,此时给集群中加⼊更多新的机器。就可以使存储的空间更⼤了
- 分布式的本质 :就是使⽤更多的机器,引⼊更多的硬件资源
- 明确:集群扩容操作,是一件风险较高,成本较大的操作,操作时,需要抱着一个敬畏的心!!!
1.把新的主节点加入到集群
-
上⾯已经把redis1-redis9重新构成了集群,接下来把redis10和redis11也加⼊集群
-
此处把redis10作为主机,redis11作为从机
plaintextredis-cli --cluster add-node 172.30.0.110:6379 172.30.0.101:6379
-
-
说明 :
add-node
后的第⼀组地址是新节点的地址- 第⼆组地址是集群中的任意节点地址,表示要把新节点加到哪个集群中
-
执行结果 :
plaintext>>> Adding node 172.30.0.110:6379 to cluster 172.30.0.101:6379 >>> Performing Cluster Check (using node 172.30.0.101:6379) M: 00d319e23ef76a4d51e74600c42ee2a371ae81f6 172.30.0.101:6379 slots:[0-5460] (5461 slots) master 2 additional replica(s) S: e34911c57d7605903de84ec05b3deac611aaef7e 172.30.0.105:6379 slots: (0 slots) slave replicates 00d319e23ef76a4d51e74600c42ee2a371ae81f6 S: 6cf48cc11d0171b6ab1b418808473167acd7986e 172.30.0.106:6379 slots: (0 slots) slave replicates 00d319e23ef76a4d51e74600c42ee2a371ae81f6 S: fd18c7f164b09ec563f4573ec9d6466e6769221e 172.30.0.108:6379 slots: (0 slots) slave replicates b3f2ba758318f4bd54031c98c01d7a6155ff43d3 M: 579282abe81b3f20ffd17d5a1956cdca3b0e71b0 172.30.0.103:6379 slots:[10923-16383] (5461 slots) master 2 additional replica(s) S: e9ea79b1326ea5a75a1701d5c12a0f6081c1d043 172.30.0.109:6379 slots: (0 slots) slave replicates 579282abe81b3f20ffd17d5a1956cdca3b0e71b0 S: 628d1ec9ceef6760b9038c4fbc83ee92430062ac 172.30.0.107:6379 slots: (0 slots) slave replicates b3f2ba758318f4bd54031c98c01d7a6155ff43d3 M: b3f2ba758318f4bd54031c98c01d7a6155ff43d3 172.30.0.102:6379 slots:[5461-10922] (5462 slots) master 2 additional replica(s) S: 2a248acb47f0036655397897f9800c70ea22514f 172.30.0.104:6379 slots: (0 slots) slave replicates 579282abe81b3f20ffd17d5a1956cdca3b0e71b0 [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered. >>> Send CLUSTER MEET to node 172.30.0.110:6379 to make it join the cluster. [OK] New node added correctly.
-
此时的集群状态 :
172.30.0.110
这个节点已经成为了集群中的主节点,但没有任何slots
- 需要重新分配
slots
- 需要重新分配
2.重新分配slots
-
示例命令 :
plaintextredis-cli --cluster reshard 172.30.0.101:6379
-
说明 :
reshard
后的地址是集群中的任意节点地址 -
执行之后,会进入交互式操作 ,Redis会提示用户输入以下内容
- 多少个slots要进⾏reshard?
- 哪个节点来接收这些slots?
- 这些slots从哪些节点搬运过来?
all
:从其他每个持有slots
的master
都拿过来点done
:从某一个或某几个节点来移动slots
,以done
为结尾
plaintextHow many slots do you want to move (from 1 to 16384)? 4096 What is the receiving node ID? 522a1bd88a1a9084e6919fa88f4bf1c3655ad837 Please enter all the source node IDs. Type 'all' to use all the nodes as source nodes for the hash slots. Type 'done' once you entered all the source nodes IDs. Source node #1: all
-
如果在搬运
slots/key
的过程中,此时客户端能否访问Redis集群呢- 对于那些不需要搬运的
key
,访问的时候是没有任何问题的 - 但是对于需要搬运的
key
,进⾏访问可能会出现短暂的访问错误(key
的位置出现了变化) - 随着搬运完成,这样的错误⾃然就恢复了
- 对于那些不需要搬运的
3.给新的主节点添加从节点
-
光有主节点了,此时扩容的⽬标已经初步达成,但是为了保证集群可⽤性,还需要给这个新的主节点添加从节点,保证该主节点宕机之后,有从节点能够顶上
-
示例命令 :
plaintextredis-cli --cluster add-node 172.30.0.111:6379 172.30.0.101:6379 --cluster-slave --cluster-master-id [172.30.1.110 节点的 nodeId]