先说结论

Redis Cluster 把键映射到 16384 个槽,再把槽分配给主节点。客户端根据槽路由请求,拓扑变化时通过 MOVED 更新长期映射;槽迁移过程中可能通过 ASK 临时访问目标节点。每个主节点可配置从节点参与故障转移。

集群使用异步复制,主节点故障时仍可能丢失尚未复制的数据,因此它提供高可用而不是强一致。

槽与多键操作

Key 的槽由 CRC16 计算;{} hash tag 可让相关 Key 落在同一槽,从而支持特定多键命令或 Lua 脚本。但过度使用同一 tag 会造成数据和流量倾斜。

槽路由的实际过程

客户端对 key 计算槽号,查询本地缓存的“槽 -> 主节点”拓扑,直接向对应节点发送命令。若拓扑已变化:

  • MOVED slot host:port 表示该槽已稳定归属新节点,客户端应更新槽映射并重试。
  • ASK slot host:port 常出现在迁移窗口,客户端临时向目标节点发送 ASKING 后重试,但不应永久更新映射。

使用 Cluster 时需要支持重定向和拓扑刷新。把普通单机 Redis 客户端直接连到一个 Cluster 节点,可能在跨槽访问时反复失败或性能异常。

主从与故障转移边界

每个槽由一个主节点提供写服务,从节点异步复制。集群节点通过 gossip 交换健康和槽信息,检测到主节点故障后,由从节点发起选举成为新主。复制是异步的:客户端刚收到写成功,主节点尚未来得及把日志复制给从节点就故障,该写入仍可能丢失。

要根据 RPO/RTO 决定副本数、故障域、写确认策略和是否允许在副本不足时继续写。Redis Cluster 提高可用性,不等同于关系数据库的强一致复制。

多 Key 与 hash tag 设计

cart:{user:1001}:items
cart:{user:1001}:meta

花括号内相同部分决定槽,两个 key 可以在同一节点执行 Lua 脚本或事务。不要把所有 key 都写成 {global},那会让整个集群退化为一个热点槽。hash tag 应只用于确实需要原子处理的一小组相关数据。

跨槽的 MGET、事务和 Lua 通常受限制。应用需要拆成多个请求再在上层合并,或重新建模,让必须原子处理的数据天然共享同一槽。

扩缩容与迁移预案

迁移一个槽需要把该槽 key 逐步从源主节点搬到目标主节点,期间会产生 ASK 重定向。大 Key、热点 Key、网络带宽和 AOF/RDB 压力会影响迁移时间。生产操作前应:

  1. 检查每节点槽数、内存、CPU、复制延迟和大 Key。
  2. 小批量迁移并观察命令延迟、重定向率和客户端错误。
  3. 确认新节点副本和故障域分布,留出故障时的接管容量。
  4. 完成后验证槽覆盖、读写一致性和客户端拓扑刷新。

缩容比扩容更危险,因为目标节点要承接全部数据与流量;不能只看平均内存,还要看热点和故障冗余。

常见线上故障

集群状态为 fail 可能是槽未完全覆盖、多个主节点不可达或网络分区;频繁 MOVED 可能是客户端没有刷新拓扑;某个节点 CPU 高而其他节点空闲往往是 Key 分布或热点问题,而非简单“集群失效”。诊断应从槽、Key 和命令维度下钻。

扩缩容

扩容的本质是把一部分槽及其 Key 从旧节点迁移到新节点,期间客户端必须正确处理重定向。操作前评估大 Key、热点、网络和持久化压力,并分批执行和校验。

容易踩坑的地方

Cluster 不代理所有请求,智能客户端需要维护拓扑。多数派故障检测与故障转移提高可用性,但网络分区、复制延迟和同时故障仍需纳入 RPO/RTO 设计。

常见问题

追问:为什么槽数不是节点数?

槽作为稳定的逻辑分片层,把数据映射与物理节点解耦,扩缩容只需迁移部分槽,不必为每个 Key 重新定义节点规则。

追问:Cluster 能保证读到主库最新数据吗?

从副本读取时可能受到复制延迟影响;读取主节点通常更接近最新已处理写,但网络、故障和客户端重试语义仍要按业务要求设计。

追问:为什么集群节点数增加后单个热 Key 还是慢?

一个 Key 不会拆到多个槽,所有命令仍由一个主节点串行处理。需要本地缓存、请求合并、读副本、Key 副本或业务分片解决。

追问:迁移时业务需要停机吗?

正常槽迁移可在线进行,客户端正确处理 ASK/MOVED 即可;但大规模迁移和资源紧张时仍可能影响延迟,应限速、灰度并准备回退。