欢迎光临
我们一直在努力

Redis分片集群故障转移

一、前言:Redis Cluster 如何实现“无人值守”高可用?

在 Redis 分片集群(Cluster)中,每个分片(shard)都由一个主节点和至少一个从节点组成。
当某个主节点因宕机、网络隔离等原因不可用时,集群必须自动选出新主,否则该分片将完全不可写。

Redis Cluster 内置了去中心化的故障检测与转移机制,无需哨兵(Sentinel),即可实现秒级自动恢复。

本文将深入解析:
✅ 故障如何被发现?
✅ 从节点如何升主?
✅ 客户端如何感知切换?
✅ 整个过程是否真的“无感”?


二、故障转移全景流程

整个过程可分为 4 个阶段:

下面我们将逐一拆解每个阶段的内部机制。


三、阶段 1:故障检测 —— 谁说了算?

3.1 检测机制:基于 gossip + 心跳

  • 每个节点每秒向其他节点发送 PING 消息
  • 若某节点 P 在 cluster-node-timeout(默认 15 秒)内未响应,则标记为 PFAIL(主观下线)

3.2 达成共识:FAIL 状态广播

  • 节点 A 发现主节点 M 失联 → 标记为 PFAIL
  • A 在后续 gossip 消息中携带 “M is PFAIL”
  • 当超过半数主节点都认为 M 失联时,A 将 M 标记为 FAIL(客观下线)
  • A 向全集群广播 FAIL <M-node-id> 消息

🔑 关键点:

  • 只有主节点参与投票(从节点不参与)
  • 需要 > N/2 主节点同意(N = 主节点总数)
  • 例如:3 主集群 → 需 2 个主节点确认

四、阶段 2:从节点发起选举

一旦主节点被标记为 FAIL,其从节点会尝试升主。

4.1 选举触发条件

  • 从节点收到 FAIL 消息
  • 自身与主节点失联时间 > repl-ping-replica-period(默认 10 秒)
  • 从节点数据较新(复制偏移量接近主)

4.2 选举流程(类 Raft)

  • 从节点将自身 currentEpoch +1,作为选举纪元
  • 向所有主节点发送 FAILOVER_AUTH_REQUEST
  • 主节点收到请求:
    • 若未投票且纪元有效 → 回复 FAILOVER_AUTH_ACK
  • 从节点获得 > N/2 主节点投票 → 当选新主
  • ✅ 举例:3 主集群 → 需 2 票才能升主


    五、阶段 3:执行故障转移

    当选从节点执行以下操作:

    步骤 1️⃣:升为主节点

    • 执行 SLAVEOF NO ONE
    • 将自己负责的 slots 状态从 importing 改为 serving

    步骤 2️⃣:广播新拓扑

    • 向全集群发送 PONG 消息,声明自己是新主
    • 更新 nodes.conf 文件(持久化配置)

    步骤 3️⃣:接管客户端请求

    • 对原主节点的 slot 范围提供读写服务

    步骤 4️⃣:原主恢复后自动变为从

    • 原主重启后,发现集群已有新主
    • 自动执行 REPLICAOF <new-master>,同步数据

    六、阶段 4:客户端如何感知切换?

    6.1 切换期间的行为

    • 客户端访问原主节点的 slot:
      • 若连接已断 → 报错 Connection refused
      • 若连接存活但主已下线 → 返回 -CLUSTERDOWN 或超时

    6.2 自动重定向(关键!)

    现代 Redis 客户端(如 Lettuce、Jedis Cluster)会:

  • 捕获连接异常
  • 主动刷新集群拓扑(通过任一存活节点执行 CLUSTER SLOTS)
  • 更新本地 slot → node 映射
  • 重试请求到新主节点
  • ✅ 效果:应用层可能短暂报错(如 RedisCommandTimeoutException),但重试后即可成功,实现“准无感”切换。

    6.3 Spring Boot 示例

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    // 即使主切换,opsForValue() 仍可工作(Lettuce 自动重连)
    public String getValue(String key) {
    try {
    return (String) redisTemplate.opsForValue().get(key);
    } catch (Exception e) {
    // 可加简单重试逻辑
    Thread.sleep(100);
    return (String) redisTemplate.opsForValue().get(key);
    }
    }


    七、实战验证:模拟主节点宕机

    7.1 环境

    • 3 主 3 从集群:7001~7006
    • 主 7001 负责 slots 0–5460

    7.2 操作步骤

    # 1. 写入测试数据
    redis-cli -c -p 7001 set test_key "before_failover"

    # 2. 杀死主节点
    kill $(pgrep -f "7001")

    # 3. 观察从节点(7004)日志
    tail -f /opt/redis-cluster/7004/redis.log
    # 应看到:Failover auth granted, Becoming a master.

    # 4. 查询新主
    redis-cli -p 7004 cluster nodes | grep master
    # 7004 应显示为 master,且负责 0-5460

    # 5. 验证数据可读写
    redis-cli -c -p 7004 get test_key # 返回 "before_failover"
    redis-cli -c -p 7004 set new_key "after" # 写入成功

    ✅ 全程耗时约 15~30 秒(取决于 cluster-node-timeout)


    八、关键配置参数

    参数默认值作用调优建议
    cluster-node-timeout 15000 ms 节点超时判定时间 网络稳定可设 10000;跨机房设 30000
    cluster-replica-validity-factor 10 从节点升主最大 lag(× timeout) 默认 10×15s=150s,可适当调大
    cluster-require-full-coverage yes 是否允许部分 slot 不可用 生产建议设为 no,避免全集群不可用

    💡 重要:cluster-require-full-coverage no
    当某个分片不可用时,其他分片仍可正常读写!


    九、常见问题排查

    ❌ 问题 1:故障转移未触发

    • 原因:主节点数 < 3,无法达成多数派
    • 解决:确保至少 3 个主节点

    ❌ 问题 2:从节点未升主

    • 原因:从节点数据太旧(lag 过大)
    • 检查:INFO REPLICATION 中 master_repl_offset

    ❌ 问题 3:客户端长时间无法连接

    • 原因:客户端未启用集群拓扑刷新
    • 解决:Lettuce 配置 refreshPeriod:

      spring:
      redis:
      lettuce:
      cluster:
      refresh-period: 30s


    十、结语

    感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!

    赞(0)
    未经允许不得转载:171主机测评 » Redis分片集群故障转移
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址