Redis Cluster:哈希槽、重定向与分片扩展(十)
当单个主库已经无法同时承受全部键空间、写吞吐和网络出口时,只增加副本并不能继续扩展写能力。Redis Cluster 的核心价值,是把键空间拆成多个分片主库,每个分片再各自拥有副本与故障转移能力。
系列导航:
- 上一篇:Redis 主从复制与 Sentinel:从复制链路到自动故障切换(九)
- 当前篇:Redis Cluster:哈希槽、重定向与分片扩展(十)(本文)
- 下一篇:Redis + .NET 实战:StackExchange.Redis 连接、读写与序列化(十一)
16384 个哈希槽是 Cluster 的路由基础
Redis Cluster 不是直接把“键永久绑定到某台机器”,而是先把全部键映射到 0 到 16383 的 16384 个哈希槽:
slot = CRC16(key) mod 16384
然后再由不同主节点分别负责其中一部分槽位。这样设计的关键好处是:
- 键到槽位的计算规则固定。
- 槽位到节点的归属可以变化。
- 扩容时只需要迁移部分槽位,而不是重算全量键路由。
所以 Cluster 真正分配的不是“某台机器负责某个前缀”,而是“某台主节点当前负责一组槽位”。
MOVED 和 ASK 反映的是两种不同的路由状态
客户端第一次访问某个键时,未必知道它对应的槽位当前在哪个主节点。Cluster 就通过重定向响应把路由信息逐步纠正回来。
MOVED:槽位的稳定归属已经改变
当目标槽位已经明确归属于另一个主节点时,Redis 会返回类似:
MOVED 12182 192.168.10.12:7000
这表示客户端应该:
- 把这次请求发到新地址。
- 更新本地维护的槽位缓存。
MOVED 说明“稳定状态已经变了”,因此是应该长期记住的新路由。
ASK:槽位还在迁移中
迁槽过程中,一部分键已经到了新节点,另一部分还在旧节点。此时旧节点可能返回:
ASK 12182 192.168.10.12:7000
这不是让客户端永久修改缓存,而是告诉它:
- 这一次先去新节点。
- 到新节点前先发送
ASKING。 - 迁移完成前,不要把整张槽位表立刻改成永久归属。
所以 MOVED 表示“归属已经稳定改变”,ASK 表示“当前处于迁移窗口中的临时通路”。
客户端路由能力是 Cluster 能不能落地的前提
Redis Cluster 没有像某些数据库那样的透明代理层。客户端要么自己理解 Cluster,要么就会不断收到重定向错误。
一个可用的 Cluster 客户端至少需要具备:
- 能缓存槽位到节点的映射。
- 能识别
MOVED和ASK并自动重试。 - 在拓扑变化后刷新路由缓存。
- 理解多键操作的槽位限制。
这也是为什么 redis-cli -c 和普通 redis-cli 的体验完全不同。前者会自动跟随重定向,后者只是把错误原样打印给你。
另外,多个键如果需要事务、Lua 或多键命令共同执行,通常还要借助哈希标签把它们放进同一个槽位,例如:
order:{1001}:header
order:{1001}:items
否则请求会因为跨槽而失败。Cluster 的路由能力不是纯粹的网络问题,它会反过来约束你的键设计和客户端选型。
扩容真正做的是“加节点 + 迁槽 + 让客户端完成路由收敛”
Redis Cluster 不是新增一个空节点就自动均衡完成。扩容通常至少包含三步:
- 把新节点加入集群。
- 为新主节点分配副本关系。
- 从现有主节点迁移一部分槽位到新主节点。
迁槽期间,客户端会看到 ASK;迁槽稳定后,新的槽位归属通过 MOVED 逐步收敛。真正需要关注的不是“容器已经多起来了”,而是:
- 16384 个槽位是否仍然完整覆盖。
- 新旧主节点的负载是否真的更均衡。
- 客户端是否能在迁槽窗口内正常自动跟随。
缩容的逻辑也是反过来:先迁走目标节点负责的全部槽位,再把节点安全移出集群。开源 Redis Cluster 并不会因为你加了机器就自动完成理想重平衡。
Cluster 的故障转移仍然建立在复制之上
每个 Cluster 主节点与自己的副本之间,底层仍然使用 Redis 复制。区别在于:这里的故障转移是“按分片独立进行”,而不是整个系统只有一个逻辑主库。
典型过程可以概括为:
- 某主节点失联后,其他节点先从本地视角把它标成疑似故障。
- 疑似故障通过集群总线传播。
- 当多数主节点对该主节点形成故障共识后,它被标记为失败。
- 该分片的某个副本争取晋升授权。
- 获得支持的副本接管原主节点负责的槽位。
这里要特别记住两点:
- 投票域看的是主节点多数派,不是副本数量多数派。
- Cluster 里的主从复制依旧是异步的,因此故障切换时同样存在少量已确认写入尚未复制完成的丢失窗口。
也就是说,Cluster 解决的是“分片 + 分片级高可用”,不是“跨分片强一致”。
什么时候该选 Cluster
如果你的问题只是“单主 Redis 需要自动故障转移”,通常先到 Redis 主从复制与 Sentinel:从复制链路到自动故障切换(九) 就够了。真正该引入 Cluster 的场景通常是:
- 数据量已经稳定超过单机可治理范围。
- 写吞吐或网络出口需要多个主节点共同承担。
- 客户端和数据模型能接受槽位限制与重定向逻辑。
- 团队能驾驭迁槽、扩缩容和跨故障域副本分布。
Cluster 的复杂度不是多几个容器那么简单,而是你开始真的管理一个“带路由、带分片、带分片级故障转移”的系统。
本篇解决了什么:
- 解释了 16384 个哈希槽为什么是 Cluster 路由与扩容的基础
- 区分了
MOVED、ASK、客户端缓存刷新和迁槽中的临时访问路径 - 说明了 Cluster 适合解决分片扩展问题,而不是替代单主自动切换的所有场景