Redis Cluster 是 Redis 官方提供的分布式数据库解决方案,它通过分片(Sharding) 来进行数据共享,并提供复制和故障转移的功能。简单来说,它解决了单机 Redis 容量有限和高可用性不足的问题。


一、为什么要使用 Redis Cluster?

在单机 Redis 模式下,我们会遇到两个核心瓶颈:

  1. 容量瓶颈:单台机器的内存容量有限,无法存储超大规模的数据集。
  2. 性能瓶颈:单个 Redis 实例的处理能力受限于单核 CPU(Redis 是单线程模型),无法应对高并发读写需求。
  3. 可用性瓶颈:虽然主从复制+哨兵模式(Sentinel)提供了高可用性,但主从模式下所有节点存储的都是全量数据,无法解决容量和写性能的问题。

Redis Cluster 的设计目标就是同时解决上述所有问题横向扩展、高可用、高性能


二、核心概念与架构

1. 数据分片 (Sharding) - 解决容量和写性能问题

Redis Cluster 将整个数据集划分为 16384 个哈希槽(Hash Slot),每个键都属于这些槽中的一个。

  • 如何分配? 使用 CRC16(key) % 16384 这个公式来计算键 key 属于哪个槽。
  • 如何管理? 集群中的每个主节点(Master)负责处理一部分哈希槽。例如,在一个有 3 个主节点的集群中:
    • 节点 A 负责处理 0 - 5500 号哈希槽。
    • 节点 B 负责处理 5501 - 11000 号哈希槽。
    • 节点 C 负责处理 11001 - 16383 号哈希槽。

这种设计的好处是:

  • 可扩展性:添加或移除节点时,只需要将一部分哈希槽从一个节点移动到另一个节点即可。在此期间,集群依然可以正常处理请求。
  • 无中心化:客户端可以直接与正确的节点通信,不需要通过代理层,避免了单点瓶颈。

2. 主从复制 (Replication) - 解决高可用问题

每个负责处理槽的主节点,都可以有 一个或多个从节点(Slave)

  • 数据同步:从节点通过异步复制的方式,同步其主节点的数据。
  • 故障转移:当某个主节点发生故障时,集群会自动将其下的某个从节点提升为新的主节点,然后由这个新主节点继续提供服务。这个过程是自动的。
  • 读写分离:客户端可以向从节点读取数据,以分担主节点的读压力(但默认情况下,客户端可能会读到旧数据,需要谨慎使用)。

一个最小的高可用 Redis Cluster 需要 3 个主节点和 3 个从节点(共 6 个节点)。

3. 集群拓扑与 Gossip 协议

Redis Cluster 是一个去中心化的架构。每个节点都保存着整个集群的状态信息,包括:

  • 所有节点的信息(IP, Port)
  • 节点负责的哈希槽
  • 节点的角色(主/从)和状态(在线、下线、疑似下线 PFAIL、已下线 FAIL)。

节点之间通过 Gossip 协议 来通信和传播集群信息。每个节点都会定期随机选择几个节点进行 PING/PONG 消息交换,以此来:

  • 检测节点是否存活。
  • 扩散集群的配置信息。
  • 识别新的节点或失效的节点。

三、工作流程与特性

1. 客户端如何访问?

客户端访问 Redis Cluster 有两种主流方式:

  1. 智能客户端 (Smart Client)

    • 客户端在启动时,会连接集群中的任意一个或多个节点,通过 CLUSTER SLOTSCLUSTER NODES 命令获取槽位映射表(Slot->Node 的映射关系),并缓存在本地。
    • 当要执行一个命令时(如 GET foo),客户端会先用 CRC16 算法计算出 foo 对应的槽(比如 1234)。
    • 然后查看本地缓存的映射表,找到负责槽 1234 的节点地址,直接向该节点发送命令。
    • 如果客户端找错了节点(例如因为集群刚刚完成了故障转移,槽位映射发生了变化),该节点会返回一个 MOVED 错误(如 MOVED 1234 192.168.1.20:6379)。客户端收到这个错误后,会更新本地的槽位映射表,然后重新向正确的节点发送请求。
  2. 代理模式 (Proxy)

    • 使用像 redis-cluster-proxy 这样的代理中间件。客户端像连接单机 Redis 一样连接代理,由代理来负责计算键的槽位、路由请求、处理重定向等。对客户端来说,集群是透明的。

推荐使用智能客户端,因为它性能更高,延迟更低。

2. 重新分片 (Resharding)

重新分片是指将哈希槽从一个节点移动到另一个节点的操作。这个过程可以在线进行,集群无需下线。

  • 何时需要? 扩容(添加新主节点)或缩容(移除主节点)时。
  • 如何操作? 使用 Redis 自带的命令行工具 redis-cli --cluster reshard
  • 过程简介
    1. 向目标节点发送准备导入的指令。
    2. 向源节点发送准备迁移的指令。
    3. 从源节点获取属于待迁移槽的键数据。
    4. 将数据发送给目标节点。
    5. 向所有相关节点广播新的槽分配信息。
  • 在迁移过程中,如果客户端访问的键已经被迁移到了新节点,源节点会返回一个 ASK 重定向错误,引导客户端去正确的节点访问。

3. 故障发现与转移

这是保证高可用的核心机制:

  1. 主观下线 (PFAIL):节点 A 通过 Ping 发现节点 B 超时无响应,节点 A 就将节点 B 标记为主观下线。
  2. 客观下线 (FAIL):节点 A 通过 Gossip 协议向集群中其他节点传播消息,询问他们对节点 B 的判断。当集群中超过半数的主节点都认为节点 B 主观下线时,节点 B 就会被标记为客观下线。
  3. 故障转移
    • 当某个主节点被标记为客观下线后,它的从节点会开始竞选成为新的主节点。
    • 从节点会向其他主节点发送投票请求。
    • 获得超过半数主节点投票的从节点将获胜,被提升为新的主节点。
    • 新主节点会接管原主节点负责的所有哈希槽,并广播通知整个集群配置已变更。

四、优点与局限性

优点:

  1. 高可用性:自动故障转移,服务中断时间极短。
  2. 横向扩展:可通过增加节点线性提升集群容量和性能。
  3. 无中心化:无需代理层,性能更高,架构更简单。
  4. 官方标准:由 Redis 官方开发和维护,兼容性好,未来有保障。

局限性与注意事项:

  1. 不支持多数据库:集群模式下只能使用 db0,不支持 SELECT 命令。
  2. 键操作限制
    • 不支持多键操作(如 MSET, MGET),除非这些键都在同一个哈希槽中。
    • 可以使用 Hash Tag 来强制将不同的键分配到同一个槽中。方法是在键中使用 {},例如 user:{1000}.profileuser:{1000}.account 会被分配到同一个槽,因为 CRC16 只会计算 {} 中间的内容 1000
  3. 事务限制:同样只能在同一节点上的键之间执行事务(通过 Hash Tag 实现)。
  4. Lua 脚本限制:所有在 Lua 脚本中操作的键必须在同一个节点上(同样可通过 Hash Tag 实现)。
  5. 资源消耗:每个节点都需要开启两个 TCP 端口(一个用于服务客户端,一个用于集群节点间通信)。
  6. 网络要求:集群所有节点间需要保持网络通畅,否则可能导致集群分裂。

五、部署与实践建议

  • 生产环境最小部署6 个节点(3主3从)。绝对不能少于 3 个主节点,否则无法完成客观下线投票。
  • 节点规划:主从节点应部署在不同的物理机上,避免宿主机宕机导致主从同时失效。
  • 客户端选择:选择成熟且积极维护的客户端库,如 Java 的 Jedis、Lettuce;Python 的 redis-py-cluster 等,它们都实现了完整的集群逻辑。
  • 监控:密切监控集群状态、节点内存使用率、键数量等指标。可以使用 redis-cli --cluster check 命令或 INFO 命令来检查集群健康状态。

总结

Redis Cluster 是 Redis 应对大规模、高可用场景的终极武器。它通过分片解决了容量和写性能问题,通过主从复制和自动故障转移解决了高可用问题。虽然它引入了一些使用上的限制(如多键操作),但其带来的横向扩展能力和高可用性是无可替代的。对于大多数大型互联网应用来说,Redis Cluster 是缓存和存储系统的最佳选择之一。