Redis Cluster通过数据分片与去中心化架构实现水平扩展和高可用,将数据分布在16384个槽位(slot)上,由多个主从节点共同承载。集群无需代理层,客户端直连任意节点,通过MOVED重定向定位目标节点。Failover故障转移机制在主节点宕机时自动选举从节点提升为主,保障服务连续性。理解槽位分配与故障转移的全流程,对Redis集群运维至关重要。
Redis Cluster集群拓扑与Gossip协议通信
Redis Cluster采用去中心化架构,节点间通过Gossip协议传播集群状态。每个节点维护一份完整的集群拓扑信息(slots map和nodes list),通过PING/PONG消息定期交换。Gossip协议最终一致性,集群状态变更后需要经过一轮传播才能全集群感知。
集群最少3主3从共6个节点,保证每个主节点至少有一个从节点。三个主节点分别负责约5461、5461、5462个槽位。客户端连接任一节点发送命令,节点根据CRC16(key) % 16384计算槽位归属,若槽位不在本节点则返回MOVED重定向。
# 创建6节点集群(3主3从)
# 每个节点redis.conf关键配置
port 7000
cluster-enabled yes
cluster-config-file nodes-7000.conf
cluster-node-timeout 5000
appendonly yes
# 使用redis-cli创建集群
redis-cli --cluster create \
127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 \
127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 \
--cluster-replicas 1
# 查看集群信息
redis-cli -p 7000 cluster info
# cluster_state:ok
# cluster_slots_assigned:16384
# cluster_slots_ok:16384
# cluster_known_nodes:6
# 查看槽位分布
redis-cli -p 7000 cluster nodes
# 节点ID IP:port flags master/slave ping/pong epoch link状态 槽位范围
# a1b2... 127.0.0.1:7000 master - 0 1234 1 connected 0-5460
# c3d4... 127.0.0.1:7001 master - 0 1235 2 connected 5461-10922
# e5f6... 127.0.0.1:7002 master - 0 1236 3 connected 10923-16383
16384槽位分配原理与迁移机制
Redis Cluster选择16384个槽位而非更大数量,是网络开销和集群规模的折中。Gossip消息中每个节点用2字节位图表示自己负责的槽位(16384 bits = 2KB),节点数较多时这个位图在每条PING/PONG中传输。若槽位数设为65536则位图占8KB,对心跳消息而言开销过大。
CRC16算法计算key的槽位:slot = CRC16(key) & 16383。使用hastag可以强制多个key分配到同一槽位:{user1000}:profile和{user1000}:settings的槽位由”user1000″子串决定,落在同一节点上,从而支持MGET、MULTI等跨Key操作。
# 槽位计算验证
redis-cli -p 7000 cluster keyslot mykey
# (integer) 5474
redis-cli -p 7000 cluster keyslot {user1000}:profile
redis-cli -p 7000 cluster keyslot {user1000}:settings
# 两者返回相同的槽位号,确保同一节点
# 手动迁移槽位(扩容场景)
# 1. 新增节点到集群
redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000
# 2. 重新分片,将部分槽位迁移到新节点
redis-cli --cluster reshard 127.0.0.1:7000 \
--cluster-from <源节点ID> \
--cluster-to <目标节点ID> \
--cluster-slots 1000 \
--cluster-yes
# 迁移单个槽位
redis-cli -p 7000 cluster setslot 8000 node <目标节点ID>
槽位迁移过程中,源节点将迁移槽位标记为MIGRATING状态,目标节点标记为IMPORTING状态。客户端请求迁移中的槽位时,源节点若找到key正常返回;若未找到则返回ASK重定向临时引导到目标节点。迁移完成后cluster setslot node确认归属变更。
Failover故障检测与自动选举流程
故障检测分两个阶段:主观下线(PFAIL)和客观下线(FAIL)。节点A向节点B发送PING,超时未收到PONG后A将B标记为PFAIL。当集群中超过半数主节点都将B标记为PFAIL时,B被标记为FAIL,触发故障转移。
故障转移由B的从节点发起。从节点检测到主节点FAIL后进入选举流程:
/* 选举流程伪代码 */
1. 从节点统计自己复制偏移量(replication offset)
2. 从节点向集群广播FAILOVER_AUTH_REQUEST
3. 其他主节点收到请求后,检查:
- 主节点B确实处于FAIL状态
- 请求从节点的复制偏移量是否最新
- 当前configEpoch是否合法
4. 主节点回复FAILOVER_AUTH_ACK(每个主节点只投一票)
5. 从节点收到过半数(N/2+1)主节点同意后胜出
6. 胜出从节点执行SLAVEOF NO ONE成为新主节点
7. 新主节点接管原主节点的所有槽位
8. 广播PONG通知全集群拓扑变更
# 手动故障转移(计划性切换,不影响服务)
redis-cli -p 7003 cluster failover
# 强制故障转移(从节点发起,不等主节点确认)
redis-cli -p 7003 cluster failover force
# 接管故障节点(极端情况,可能丢数据)
redis-cli -p 7003 cluster failover takeover
# 查看故障转移状态
redis-cli -p 7003 cluster failover status
手动failover会先同步完主节数据再切换,数据零丢失。force模式跳过主节点握手直接选举,适用于主节点不可达但数据已同步的场景。takeover模式最危险,跳过选举直接提升,可能导致脑裂,仅用于紧急恢复。
脑裂问题与集群配置参数调优
网络分区可能导致集群分裂为多个少数派分区。由于Failover需要过半主节点投票,少数派分区无法完成选举,主节点继续服务但无法获知另一分区的Failover。当分区恢复后,原主节点的写入会因configEpoch较低而被丢弃。
cluster-node-timeout是最关键参数,控制节点失联超时时间。设过短导致网络抖动误报故障,设过长则故障检测延迟。生产环境推荐5000ms~10000ms。调优参考公式:timeout = network_rtt_max * 3 + heartbeat_interval。
# 关键集群参数
cluster-node-timeout 5000 # 节点超时(ms)
cluster-replica-validity-factor 10 # 从节点参与选举的有效因子
# (timeout * factor 内复制断开的从节点不参与选举)
cluster-migration-barrier 1 # 主节点最少保留从节点数
cluster-require-full-coverage yes # 槽位未全覆盖时拒绝服务
cluster-allow-reads-when-down no # 集群下线时是否允许读
# min-replicas配置(主节点写入需从节点确认)
min-replicas 1 # 至少1个从节点确认才接受写入
min-replicas-max-lag 10 # 从节点最大延迟秒数
cluster-require-full-coverage设为yes时,任一槽位无主节点服务则整个集群拒绝写入。对可用性要求高于一致性的场景可设为no,允许部分槽位不可用时其他槽位继续服务。
客户端集群路由与连接池配置
Redis Cluster客户端需实现MOVED/ASK重定向逻辑。初始化时获取集群拓扑缓存到本地,遇到MOVED更新本地路由表。Lettuce/Jedis等Java客户端已内置Cluster支持:
// Spring Boot + Lettuce配置
@Configuration
public class RedisClusterConfig {
@Bean
public RedisConnectionFactory redisConnectionFactory() {
RedisClusterConfiguration config = new RedisClusterConfiguration()
.clusterNode("192.168.1.10", 7000)
.clusterNode("192.168.1.10", 7001)
.clusterNode("192.168.1.11", 7000);
config.setMaxRedirects(3); // MOVED重定向最大次数
LettuceConnectionFactory factory = new LettuceConnectionFactory(config);
factory.setValidateConnection(true);
return factory;
}
}
# application.yml 配置方式
spring:
data:
redis:
cluster:
nodes: 192.168.1.10:7000,192.168.1.10:7001,192.168.1.11:7000
max-redirects: 3
lettuce:
pool:
max-active: 16 # 每节点连接池大小
max-idle: 8
min-idle: 2
cluster:
refresh:
adaptive: true # 自适应刷新拓扑
period: 30s # 定时刷新间隔
adaptive: true让客户端在检测到MOVED或连接异常时自动刷新集群拓扑,period: 30s作为定时刷新兜底。连接池大小按单节点配置,集群总连接数 = pool_size * node_count,需确保不exceeding服务端maxclients限制(默认10000)。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/rediscluster-ji-qun-cao-wei-fen-pei-ji-zhi-yu-failover-gu/