Redis Cluster集群模式
Redis Cluster 是 Redis 官方提供的分布式数据库解决方案,它通过分片(Sharding) 来进行数据共享,并提供复制和故障转移的功能。简单来说,它解决了单机 Redis 容量有限和高可用性不足的问题。
一、为什么要使用 Redis Cluster?
在单机 Redis 模式下,我们会遇到两个核心瓶颈:
- 容量瓶颈:单台机器的内存容量有限,无法存储超大规模的数据集。
- 性能瓶颈:单个 Redis 实例的处理能力受限于单核 CPU(Redis 是单线程模型),无法应对高并发读写需求。
- 可用性瓶颈:虽然主从复制+哨兵模式(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 有两种主流方式:
智能客户端 (Smart Client):
- 客户端在启动时,会连接集群中的任意一个或多个节点,通过
CLUSTER SLOTS或CLUSTER NODES命令获取槽位映射表(Slot->Node 的映射关系),并缓存在本地。 - 当要执行一个命令时(如
GET foo),客户端会先用 CRC16 算法计算出foo对应的槽(比如 1234)。 - 然后查看本地缓存的映射表,找到负责槽 1234 的节点地址,直接向该节点发送命令。
- 如果客户端找错了节点(例如因为集群刚刚完成了故障转移,槽位映射发生了变化),该节点会返回一个 MOVED 错误(如
MOVED 1234 192.168.1.20:6379)。客户端收到这个错误后,会更新本地的槽位映射表,然后重新向正确的节点发送请求。
- 客户端在启动时,会连接集群中的任意一个或多个节点,通过
代理模式 (Proxy):
- 使用像
redis-cluster-proxy这样的代理中间件。客户端像连接单机 Redis 一样连接代理,由代理来负责计算键的槽位、路由请求、处理重定向等。对客户端来说,集群是透明的。
- 使用像
推荐使用智能客户端,因为它性能更高,延迟更低。
2. 重新分片 (Resharding)
重新分片是指将哈希槽从一个节点移动到另一个节点的操作。这个过程可以在线进行,集群无需下线。
- 何时需要? 扩容(添加新主节点)或缩容(移除主节点)时。
- 如何操作? 使用 Redis 自带的命令行工具
redis-cli --cluster reshard。 - 过程简介:
- 向目标节点发送准备导入的指令。
- 向源节点发送准备迁移的指令。
- 从源节点获取属于待迁移槽的键数据。
- 将数据发送给目标节点。
- 向所有相关节点广播新的槽分配信息。
- 在迁移过程中,如果客户端访问的键已经被迁移到了新节点,源节点会返回一个 ASK 重定向错误,引导客户端去正确的节点访问。
3. 故障发现与转移
这是保证高可用的核心机制:
- 主观下线 (PFAIL):节点 A 通过 Ping 发现节点 B 超时无响应,节点 A 就将节点 B 标记为主观下线。
- 客观下线 (FAIL):节点 A 通过 Gossip 协议向集群中其他节点传播消息,询问他们对节点 B 的判断。当集群中超过半数的主节点都认为节点 B 主观下线时,节点 B 就会被标记为客观下线。
- 故障转移:
- 当某个主节点被标记为客观下线后,它的从节点会开始竞选成为新的主节点。
- 从节点会向其他主节点发送投票请求。
- 获得超过半数主节点投票的从节点将获胜,被提升为新的主节点。
- 新主节点会接管原主节点负责的所有哈希槽,并广播通知整个集群配置已变更。
四、优点与局限性
优点:
- 高可用性:自动故障转移,服务中断时间极短。
- 横向扩展:可通过增加节点线性提升集群容量和性能。
- 无中心化:无需代理层,性能更高,架构更简单。
- 官方标准:由 Redis 官方开发和维护,兼容性好,未来有保障。
局限性与注意事项:
- 不支持多数据库:集群模式下只能使用
db0,不支持SELECT命令。 - 键操作限制:
- 不支持多键操作(如
MSET,MGET),除非这些键都在同一个哈希槽中。 - 可以使用 Hash Tag 来强制将不同的键分配到同一个槽中。方法是在键中使用
{},例如user:{1000}.profile和user:{1000}.account会被分配到同一个槽,因为 CRC16 只会计算{和}中间的内容1000。
- 不支持多键操作(如
- 事务限制:同样只能在同一节点上的键之间执行事务(通过 Hash Tag 实现)。
- Lua 脚本限制:所有在 Lua 脚本中操作的键必须在同一个节点上(同样可通过 Hash Tag 实现)。
- 资源消耗:每个节点都需要开启两个 TCP 端口(一个用于服务客户端,一个用于集群节点间通信)。
- 网络要求:集群所有节点间需要保持网络通畅,否则可能导致集群分裂。
五、部署与实践建议
- 生产环境最小部署:6 个节点(3主3从)。绝对不能少于 3 个主节点,否则无法完成客观下线投票。
- 节点规划:主从节点应部署在不同的物理机上,避免宿主机宕机导致主从同时失效。
- 客户端选择:选择成熟且积极维护的客户端库,如 Java 的 Jedis、Lettuce;Python 的 redis-py-cluster 等,它们都实现了完整的集群逻辑。
- 监控:密切监控集群状态、节点内存使用率、键数量等指标。可以使用
redis-cli --cluster check命令或INFO命令来检查集群健康状态。
总结
Redis Cluster 是 Redis 应对大规模、高可用场景的终极武器。它通过分片解决了容量和写性能问题,通过主从复制和自动故障转移解决了高可用问题。虽然它引入了一些使用上的限制(如多键操作),但其带来的横向扩展能力和高可用性是无可替代的。对于大多数大型互联网应用来说,Redis Cluster 是缓存和存储系统的最佳选择之一。


