Redis手动进行主从切换
# Redis手动进行主从切换
# 前言
当redis集群中的一个节点出现故障,需要将主从互换的时候会用到
版本说明
本文写于 2022-03。
主从切换命令(SLAVEOF/REPLICAOF)与流程未变,经 2026-07 复核仍适用。
[dba@redisnode0001 bin]# sudo ./redis-cli -h <PUBLIC_IP> -p 8001 -c -a xxxxxxxxxxx --cluster check 127.0.0
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
<PUBLIC_IP>:9001 (c1422bc2...) -> 1 keys | 5461 slots | 1 slaves.
<PUBLIC_IP>:9002 (87f4cf99...) -> 0 keys | 5462 slots | 1 slaves.
<PUBLIC_IP>:9003 (63395075...) -> 1 keys | 5461 slots | 1 slaves.
[OK] 2 keys in 3 masters.
0.00 keys per slot on average.
>>> Performing Cluster Check (using node <PUBLIC_IP>:9001)
M: c1422bc23f702c702a1de872f5a0026238268fa9 <PUBLIC_IP>:9001
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: c1000a4985fd5a8c8afd9c8bdaa4d99f137e78eb <PUBLIC_IP>:9003
slots: (0 slots) slave
replicates 633950751a8fc0a18b4a1ad4c5dd90095d4147b0
M: 87f4cf99d533f74532d5b586bbdeabe7dc0df499 <PUBLIC_IP>:9002
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
M: 633950751a8fc0a18b4a1ad4c5dd90095d4147b0 <PUBLIC_IP>:9003
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
S: 0ca6df3fb57b53700bb2d328696932d3f3fe62c9 <PUBLIC_IP>:9001
slots: (0 slots) slave
replicates c1422bc23f702c702a1de872f5a0026238268fa9
S: d6f6aa0a5003a25060e48032544f27044d5445ef <PUBLIC_IP>:9002
slots: (0 slots) slave
replicates 87f4cf99d533f74532d5b586bbdeabe7dc0df499
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
通过check 命令找到与主节点相同ID的从节点,连接上从节点之后执行命令
[dba@redisnode0001 bin]# sudo ./redis-cli -h <PUBLIC_IP> -p 9001 -c -a xxxxxxxxxxxxx
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
<PUBLIC_IP>:9001> cluster failover
OK
<PUBLIC_IP>:9001> cluster nodes
87f4cf99d533f74532d5b586bbdeabe7dc0df499 <PUBLIC_IP>:9002@19002 master - 0 1638603378000 2 connected 5461-10922
633950751a8fc0a18b4a1ad4c5dd90095d4147b0 <PUBLIC_IP>:9003@19003 master - 0 1638603377000 3 connected 10923-16383
0ca6df3fb57b53700bb2d328696932d3f3fe62c9 <PUBLIC_IP>:9001@19001 myself,master - 0 1638603377000 5 connected 0-5460
c1422bc23f702c702a1de872f5a0026238268fa9 <PUBLIC_IP>:9001@19001 slave 0ca6df3fb57b53700bb2d328696932d3f3fe62c9 0 1638603378554 5 connected
d6f6aa0a5003a25060e48032544f27044d5445ef <PUBLIC_IP>:9002@19002 slave 87f4cf99d533f74532d5b586bbdeabe7dc0df499 0 1638603378000 2 connected
c1000a4985fd5a8c8afd9c8bdaa4d99f137e78eb <PUBLIC_IP>:9003@19003 slave 633950751a8fc0a18b4a1ad4c5dd90095d4147b0 0 1638603378752 3 connected
2
3
4
5
6
7
8
9
10
11
若无意在主节点上执行的话,会有以下报错
<PUBLIC_IP>:8001> cluster failover
(error) ERR You should send CLUSTER FAILOVER to a replica
2
# 为什么需要手动 failover
Redis Cluster 的故障转移默认是自动的:当主节点被判定为 fail(cluster-node-timeout 内无响应,且多数主节点认可),其从节点会自动提升为主节点。
手动 failover 的场景:
- 计划内维护:需要重启主节点所在的机器,手动 failover 可以把影响降到毫秒级(自动 failover 需要等
cluster-node-timeout)。 - 主节点性能异常:CPU/网络抖动但不至于被判定为
fail,手动切换让从节点接管。 - 机架迁移:把主节点集中到特定机架,需要通过 failover 重新分配主从分布。
- 硬件更换:更换主板、内存、磁盘等需要停机的操作,提前把业务切换到从节点。
- 版本升级:Redis 升级通常需要重启,手动 failover 实现平滑升级。
与自动 failover 的区别:手动 failover 是强制且立即的,不需要等超时,也不需要多数主节点认可——它假设你确认当前主节点需要被替换。
# 原理:Redis Cluster 故障转移机制
Redis Cluster 的故障转移分为自动和手动两种,它们的底层机制有显著差异:
自动 failover 流程:
- 主节点被标记为
fail:至少需要多数主节点(N/2+1)在cluster-node-timeout时间内确认该节点不可达。 - 从节点发起选举:所有从节点中,复制偏移量最大的从节点获得优先选举权。
- 广播选举结果:获胜的从节点向集群所有节点广播
PONG,声明自己成为新的主节点。 - 槽位迁移:原主节点的槽位全部迁移到新主节点。
手动 failover(CLUSTER FAILOVER)流程:
- 从节点向主节点发送
CLUSTER FAILOVER命令。 - 主节点阻塞当前复制流,将当前的复制偏移量发送给从节点。
- 从节点等待接收主节点的复制流,直到与主节点复制进度对齐。
- 从节点宣布自己为新的主节点,并广播更新后的集群配置。
- 原主节点降级为从节点,开始复制新的主节点。
# 验证切换成功
执行 cluster failover 后,立即验证:
redis-cli -h <target-node> -p <port> cluster nodes
判读要点:
- 原从节点:
myself,master表示它已提升为主节点,且最后一列显示槽位范围(如0-5460)。 - 原主节点:显示为
slave,且replicates指向新的主节点 node-id。 - cluster_state:
cluster info | grep cluster_state应返回ok。
# 完整验证脚本
#!/bin/bash
# 验证 Redis Cluster 主从切换是否成功
CLUSTER_HOST="127.0.0.1"
CLUSTER_PORT=9001
echo "=== 1. 检查集群状态 ==="
redis-cli -h $CLUSTER_HOST -p $CLUSTER_PORT cluster info | grep cluster_state
echo -e "\n=== 2. 检查所有节点角色 ==="
redis-cli -h $CLUSTER_HOST -p $CLUSTER_PORT cluster nodes | \
awk '{print $1, $2, $3, $4, $NF}'
echo -e "\n=== 3. 检查槽位覆盖 ==="
redis-cli -h $CLUSTER_HOST -p $CLUSTER_PORT cluster info | grep cluster_slots_ok
echo -e "\n=== 4. 模拟业务写入测试 ==="
redis-cli -h $CLUSTER_HOST -p $CLUSTER_PORT -c set test_key_$(date +%s) verification_value
redis-cli -h $CLUSTER_HOST -p $CLUSTER_PORT -c get test_key_*
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 自动化健康检查命令
# 一键检查集群健康状态
redis-cli --cluster check <HOST>:<PORT> 2>&1 | grep -E "(OK|All nodes|WARNING|FAIL)"
# 检查所有主节点的槽位是否都有从节点接管
redis-cli -h <HOST> -p <PORT> cluster nodes | grep -v "slave" | grep "master" | wc -l
# 检查每个主节点是否至少有一个健康的从节点
redis-cli -h <HOST> -p <PORT> cluster nodes | grep "slave" | grep -v "fail" | wc -l
2
3
4
5
6
7
8
# 坑与边界
脑裂风险:手动 failover 期间,如果原主节点仍在写数据(比如你只断了它的集群总线但没停客户端连接),会导致双主局面——两边都有最新数据,但复制方向已切换,数据会分歧。执行 failover 前务必确认原主节点已停止接受写请求(
CLIENT PAUSE或直接停服)。防御措施:
# 在原主节点上暂停所有客户端写入 redis-cli -h <old-master> -p 8001 CLIENT PAUSE 30000 WRITE # 确认暂停成功 redis-cli -h <old-master> -p 8001 CLIENT LIST | grep flags # 应该看到 'N' (normal) 仍然存在,但写入已阻塞1
2
3
4
5
6自动提升的竞态:如果手动 failover 执行时,自动故障转移也在进行(比如原主节点刚好超时),可能导致 failover 失败或产生意外的拓扑。建议在业务低峰期执行,并提前把
cluster-node-timeout临时调大(执行完再调回)。# 临时调大超时时间(生产环境慎用) CONFIG SET cluster-node-timeout 30000 # 执行完手动 failover 后恢复 CONFIG SET cluster-node-timeout 150001
2
3
4
5从节点复制延迟:如果从节点落后主节点太多(
master_repl_offset差距大),failover 后这部分未复制的数据会丢失。执行前先检查偏移量:# 在从节点上检查复制延迟 redis-cli info replication | grep -E "master_link_status|master_repl_offset|slave_read_repl_offset" # 在主节点上检查当前偏移量 redis-cli info replication | grep master_repl_offset1
2
3
4
5客户端重定向:failover 完成后,客户端需要收到
MOVED重定向才能正确路由。大多数 Redis 客户端会自动跟随,但部分老旧客户端可能需要重建连接。建议在切换后监控客户端日志,确认没有大量MOVED错误。failover 不是回滚操作:一旦执行,原主节点变成从节点,数据流向反转。如果你想恢复原来的主从关系,需要在新主节点上再执行一次
cluster failover(它现在是主节点,需要先cluster failover TAKEOVER强制夺回主权——但这会丢失新主节点上的写入)。权限要求:执行 failover 的从节点必须能与多数主节点通信,否则无法完成选举。如果集群正处于分区状态(网络分裂),failover 会报错。
数据丢失场景:手动 failover 在以下场景会导致数据丢失:
- 从节点复制缓冲已满(
client-output-buffer-limit replica配置过小) - 网络分区期间客户端仍写入原主节点
- 从节点接收复制流前被强制提升
- 从节点复制缓冲已满(
执行频率限制:Redis Cluster 不建议频繁执行手动 failover,每次切换都会触发槽位迁移和客户端重定向,频繁切换会导致:
- 客户端频繁收到
MOVED重定向 - 集群拓扑频繁变化,增加网络开销
- 复制延迟累积
- 客户端频繁收到
# 可复用要点
手动 failover 最适合计划内维护场景:相比自动 failover 需要等待
cluster-node-timeout才能触发,手动 failover 可以做到毫秒级切换,业务感知几乎为零。failover 前必做三件事:
- 确认从节点与主节点的复制延迟在可接受范围(
master_repl_offset差值 < 1000) - 暂停原主节点的写入(
CLIENT PAUSE或关闭客户端连接) - 记录切换前的完整拓扑(
cluster nodes输出)
- 确认从节点与主节点的复制延迟在可接受范围(
切换后的验证清单:
cluster_state必须为ok- 所有 16384 个槽位都有明确归属
- 每个主节点都有健康的从节点
- 客户端可以正常写入和读取
回滚方案:如果切换后发现问题,需要在新主节点上再次执行
cluster failover TAKEOVER强制夺回主权,但会丢失切换期间新主节点的写入数据。监控告警建议:在执行手动 failover 时,建议同时监控以下指标:
cluster_cluster_slots_assigned:16384 表示所有槽位已分配cluster_cluster_slots_ok:每个主节点槽位状态健康replication_master_repl_offset:主从复制进度对比