Redis Cluster 概览以及Spring Boot 中的使用 
Redis Cluster 概览 - 目的:水平扩展+高可用。数据按 16384 个 slot 分片(slot = CRC16(key) % 16384),各主节点持有一部分 slot;每个主节点有 0..N 个从节点。
 - 客户端路由:请求发到任意节点,若不在本节点,该节点返回 MOVED/ASK,客户端重定向到负责该 slot 的主节点(“智能客户端”负责处理)。
 - 容错:主从复制+投票。超过半数主节点存活即可工作;某主节点失联时,对应从节点自动升级为主(failover)。
 - 伸缩:迁移 slot(reshard/rebalance)即可;迁移期间会返回 ASK。
 - 端口:客户端端口 P(如 6379),集群总线端口 P+10000(gossip/投票)。
 - 重要限制
 
- 只有 DB 0,没有 SELECT 切库。
 - 多 key 命令/事务/Lua 只能作用于同一 slot;否则报 CROSSSLOT。用哈希标签强制同槽:key 形如 {tag}:field1 与 {tag}:field2。
 - Pub/Sub 是“节点局部”的;要全局广播需在所有节点订阅或用更高层库(如 Redisson)。
 - 不支持 KEYS 全量扫描(每节点自己扫);SCAN 需逐节点汇总。
 
 
  
 常用管理 - 创建集群
 
- 多台机器各启动一个 redis(开启 cluster-enabled yes,设置 cluster-config-file 和 port)
 - 执行:redis-cli --cluster create host1:6379 host2:6379 host3:6379 host4:6379 host5:6379 host6:6379 --cluster-replicas 1
 
 
  - 查看/维护
 
- redis-cli -c -h host -p 6379
 - CLUSTER NODES / CLUSTER INFO
 - 迁移 slot:redis-cli --cluster reshard host:6379
 - 添加/删除节点:redis-cli --cluster add-node / del-node
 
 
  
 Spring Boot 中的使用(Lettuce,推荐) - spring:
 
 -   redis:
 
 -     password: yourpwd
 
 -     timeout: 3000ms
 
 -     cluster:
 
 -       nodes:
 
 -         - 10.0.0.1:6379
 
 -         - 10.0.0.2:6379
 
 -         - 10.0.0.3:6379
 
 -         - 10.0.0.4:6379
 
 -       max-redirects: 5
 
 -     lettuce:
 
 -       pool:
 
 -         max-active: 16
 
 -         max-idle: 8
 
 -         min-idle: 0
 
 -       cluster:
 
 -         refresh:
 
 -           adaptive: true    # 探测 MOVED/FAIL 后自动刷新拓扑
 
 -           period: 30s       # 周期刷新拓扑
 
  复制代码- import org.springframework.context.annotation.*;
 
 - import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
 
 - import org.springframework.data.redis.connection.*;
 
 - import org.springframework.data.redis.connection.lettuce.*;
 
 - import io.lettuce.core.cluster.ClusterClientOptions;
 
 - import io.lettuce.core.cluster.topology.*;
 
  
- @Configuration
 
 - public class RedisClusterConfig {
 
  
-   @Bean
 
 -   public LettuceConnectionFactory redisConnectionFactory(RedisProperties props) {
 
 -     RedisClusterConfiguration cluster = new RedisClusterConfiguration(props.getCluster().getNodes());
 
 -     cluster.setMaxRedirects(props.getCluster().getMaxRedirects());
 
 -     if (props.getPassword()!=null) cluster.setPassword(RedisPassword.of(props.getPassword()));
 
  
-     ClusterTopologyRefreshOptions topo = ClusterTopologyRefreshOptions.builder()
 
 -         .enableAdaptiveRefreshTrigger(ClusterTopologyRefreshOptions.RefreshTrigger.MOVED_REDIRECT,
 
 -                                       ClusterTopologyRefreshOptions.RefreshTrigger.PERSISTENT_RECONNECTS)
 
 -         .enablePeriodicRefresh(java.time.Duration.ofSeconds(30))
 
 -         .build();
 
  
-     LettuceClientConfiguration client = LettuceClientConfiguration.builder()
 
 -         .clientOptions(ClusterClientOptions.builder().topologyRefreshOptions(topo).build())
 
 -         .commandTimeout(java.time.Duration.ofSeconds(3))
 
 -         .build();
 
  
-     return new LettuceConnectionFactory(cluster, client);
 
 -   }
 
 - }
 
  复制代码- RedisTemplate(JSON 与二进制;注意:Cluster 没有多 DB,两个模板共用同一工厂)
 
 
 - import org.springframework.context.annotation.*;
 
 - import org.springframework.data.redis.core.RedisTemplate;
 
 - import org.springframework.data.redis.connection.RedisConnectionFactory;
 
 - import org.springframework.data.redis.serializer.*;
 
  
- @Configuration
 
 - public class RedisTemplatesConfig {
 
  
-   @Bean
 
 -   public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory cf) {
 
 -     RedisTemplate<String, Object> t = new RedisTemplate<>();
 
 -     t.setConnectionFactory(cf);
 
 -     t.setKeySerializer(StringRedisSerializer.UTF_8);
 
 -     t.setHashKeySerializer(StringRedisSerializer.UTF_8);
 
 -     GenericJackson2JsonRedisSerializer json = new GenericJackson2JsonRedisSerializer();
 
 -     t.setValueSerializer(json);
 
 -     t.setHashValueSerializer(json);
 
 -     t.afterPropertiesSet();
 
 -     return t;
 
 -   }
 
  
-   @Bean(name = "binaryRedisTemplate")
 
 -   public RedisTemplate<String, byte[]> binaryRedisTemplate(RedisConnectionFactory cf) {
 
 -     RedisTemplate<String, byte[]> t = new RedisTemplate<>();
 
 -     t.setConnectionFactory(cf);
 
 -     t.setKeySerializer(StringRedisSerializer.UTF_8);
 
 -     t.setHashKeySerializer(StringRedisSerializer.UTF_8);
 
 -     t.setValueSerializer(RedisSerializer.byteArray());
 
 -     t.setHashValueSerializer(RedisSerializer.byteArray());
 
 -     t.afterPropertiesSet();
 
 -     return t;
 
 -   }
 
 - }
 
  复制代码- 关键点/最佳实践
 
- 不能用不同 DB 索引区分数据;用 key 前缀区分业务,如 app:bin:{tag}:...
 - 需要对同一业务对象的多 key 操作(MGET/MSET、Lua、事务),把相关 key 放同槽:使用哈希标签
 
- 示例:"{order:123}:status" 与 "{order:123}:total" 在同一 slot,可一起 MGET/MSET。
 
 
  - 分布式锁/延迟队列等,优先用 Redisson(对 Cluster 细节做了适配)。
 - Pub/Sub 全局广播:在所有节点订阅,或用 Redisson 的 topic。
 - 观察指标:CLUSTER INFO(cluster_state, slots_assigned, slots_ok, slots_pfail), INFO replication、latency、connected_clients。
 
 
  
 Redisson(可选,高层封装) - // 仅示例
 
 - org.redisson.config.Config c = new org.redisson.config.Config();
 
 - c.useClusterServers()
 
 -  .addNodeAddress("redis://10.0.0.1:6379","redis://10.0.0.2:6379","redis://10.0.0.3:6379")
 
 -  .setPassword("yourpwd");
 
 - org.redisson.api.RedissonClient redisson = org.redisson.Redisson.create(c);
 
 - // 分布式锁
 
 - org.redisson.api.RLock lock = redisson.getLock("{order:123}:lock");
 
 - lock.lock();
 
 - try { /* ... */ } finally { lock.unlock(); }
 
  复制代码排错提示 - 报 CROSSSLOT:相关 key 不同 slot;用 {tag} 使其同槽。
 - 报 MOVED/ASK 循环:客户端未启用 cluster 模式或拓扑未刷新;确保使用 Lettuce/Jedis 的 cluster 客户端,并开启拓扑刷新。
 - 命令不支持:Cluster 不支持 KEYS/SCAN 全局;改为逐节点遍历或用二级索引。
 
 
  |