Redis Cluster是Redis官方提供的分布式方案,通过数据分片实现水平扩展,通过主从复制保障高可用。集群将所有数据划分为16384个槽位(slot),每个节点负责一部分槽位。写入数据时根据CRC16算法计算key的槽位号,路由到对应节点。当节点故障时,其从节点自动提升为主节点接管槽位,实现自动故障转移。本文从集群搭建到故障转移配置,完整演示Redis Cluster的生产级部署。
Redis Cluster数据分片与槽位映射原理
Redis Cluster采用哈希分片策略。key到slot的映射公式:slot = CRC16(key) mod 16384。CRC16算法对key的每个字节计算,生成16位哈希值,对16384取模得到槽位号。每个节点维护一份cluster slots映射表,记录哪些槽位属于哪个节点。
客户端可以连接任意节点发起请求。如果key所在槽位不在当前节点,节点返回MOVED重定向响应,包含目标节点地址。客户端根据MOVED响应更新本地槽位映射缓存,后续请求直接发送到正确节点。Smart Client(如Jedis Cluster、Lettuce)在初始化时拉取完整槽位映射表,运行期自动维持缓存同步,大幅减少重定向开销。
当多个key分布在不同槽位时,跨槽位操作(MGET、SUNION等)会报错。Redis Cluster通过Hash Tag机制支持多key操作——key中{…}包裹的部分参与哈希计算,如user:{1001}:name和user:{1001}:age映射到同一槽位,可以批量操作。
集群搭建与节点配置
最小Redis Cluster需要6个节点(3主3从)。以Docker方式搭建6节点集群:
# 每个节点的redis.conf基础配置
port 7000
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
appendfsync everysec
# docker-compose.yml 创建6个Redis节点
version: "3.8"
services:
redis-7000:
image: redis:7.2
command: redis-server --port 7000 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes
ports: ["7000:7000"]
redis-7001:
image: redis:7.2
command: redis-server --port 7001 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes
ports: ["7001:7001"]
redis-7002:
image: redis:7.2
command: redis-server --port 7002 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes
ports: ["7002:7002"]
redis-7003:
image: redis:7.2
command: redis-server --port 7003 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes
ports: ["7003:7003"]
redis-7004:
image: redis:7.2
command: redis-server --port 7004 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes
ports: ["7004:7004"]
redis-7005:
image: redis:7.2
command: redis-server --port 7005 --cluster-enabled yes --cluster-config-file nodes.conf --cluster-node-timeout 5000 --appendonly yes
ports: ["7005:7005"]
使用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
# --cluster-replicas 1 表示每个主节点1个从节点
# 集群自动分配:
# 7000(M) -> 7003(S)
# 7001(M) -> 7004(S)
# 7002(M) -> 7005(S)
# 槽位分配:0-5460(7000), 5461-10922(7001), 10923-16383(7002)
集群状态检查与槽位管理命令
# 查看集群信息
redis-cli -p 7000 cluster info
# cluster_state:ok
# cluster_slots_assigned:16384
# cluster_slots_ok:16384
# cluster_known_nodes:6
# cluster_size:3
# 查看节点列表
redis-cli -p 7000 cluster nodes
# 输出格式:ID IP:PORT@CPORT flags master-id ping pong epoch link
# 查看槽位分配
redis-cli -p 7000 cluster slots
# 检查集群一致性
redis-cli --cluster check 127.0.0.1:7000
# 查看key所在槽位
redis-cli -p 7000 cluster keyslot mykey
# (integer) 5474
在线扩容:添加节点与槽位迁移
向集群添加新节点时,需要将部分槽位从现有节点迁移到新节点,整个过程在线进行不影响业务。
# 1. 添加新主节点( initially无槽位)
redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000
# 2. 为新主节点添加从节点
redis-cli --cluster add-node 127.0.0.1:7007 127.0.0.1:7000 --cluster-slave --cluster-master-id <7006节点ID>
# 3. 重新分片,从现有3个主节点各迁移部分槽位到新节点
redis-cli --cluster reshard 127.0.0.1:7000 \
--cluster-from <7000节点ID>,<7001节点ID>,<7002节点ID> \
--cluster-to <7006节点ID> \
--cluster-slots 4096 \
--cluster-yes
# 4. 验证槽位分布
redis-cli --cluster check 127.0.0.1:7000
槽位迁移过程中,每个key的迁移是原子操作——源节点将key序列化发送到目标节点,目标节点确认后源节点删除本地副本。迁移期间对该key的请求由源节点代理转发到目标节点,客户端无感知。cluster-require-full-coverage默认为yes,即任意节点故障导致槽位不可达时整个集群不可写。生产环境可设为no,允许部分槽位不可用时其他槽位正常服务。
故障检测与自动故障转移机制
Redis Cluster通过Gossip协议进行节点间通信和故障检测。每个节点定期向其他节点发送PING消息,收到PONG响应确认存活。cluster-node-timeout参数控制超时阈值(默认15秒,生产环境推荐5-10秒),超过该时间未收到响应则将目标节点标记为PFAIL(疑似故障)。
PFAIL状态需要经过集群多数派确认才升级为FAIL(确定故障)。当主节点被标记为FAIL后,其从节点发起故障转移:
1. 从节点检测到主节点FAIL状态
2. 从节点等待一段随机延迟(避免多个从节点同时竞选),延迟时间与从节点复制偏移量相关——偏移量越大(数据越新)延迟越短
3. 从节点向集群发起选举请求,获取多数派主节点投票
4. 赢得选举的从节点执行SLAVEOF NO ONE升级为主节点
5. 新主节点广播PONG消息通知集群槽位归属变更
# 手动故障转移(计划性维护)
redis-cli -p 7003 cluster failover
# 7003为从节点,执行后它将替代主节点
# 手动故障转移可选模式:
# cluster failover takeover - 强制接管,不经过选举(紧急恢复用)
# cluster failover force - 跳过主节点确认直接选举
# 查看故障转移后拓扑
redis-cli -p 7000 cluster nodes
客户端连接与读写分离配置
以Java Lettuce客户端为例配置Redis Cluster连接:
// Spring Boot application.yml
spring:
data:
redis:
cluster:
nodes: 127.0.0.1:7000,127.0.0.1:7001,127.0.0.1:7002
max-redirects: 3
timeout: 3000ms
lettuce:
pool:
max-active: 16
max-idle: 8
min-idle: 2
# 开启读写分离(从节点读取)
read-from: REPLICA_PREFERRED
// RedisTemplate配置
@Configuration
public class RedisClusterConfig {
@Bean
public RedisTemplate redisTemplate(
RedisConnectionFactory connectionFactory) {
RedisTemplate template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setEnableTransactionSupport(false);
return template;
}
}
read-from=REPLICA_PREFERRED优先从从节点读取,主节点仅在无可用从节点时使用。读写分离适用于读多写少且能容忍主从延迟的场景。需要注意从节点的数据存在复制延迟(通常毫秒级),对强一致性要求高的读操作应从主节点读取(用READONLY命令标记后从节点也会返回数据,但可能是旧数据)。
max-redirects=3设置MOVED和ASK重定向的最大次数。超过重定向次数仍无法到达正确节点时抛出异常。正常情况下Smart Client维持本地槽位映射缓存,重定向仅在集群拓扑变更时偶发,3次够用。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/rediscluster-ji-qun-jia-gou-yu-cao-wei-fen-pei-gu-zhang/