引言Redis 集群主要有两种形态主从哨兵用来保障高可用切片集群用来实现水平扩展前者通过哨兵监控与自动切换解决单点故障问题后者基于哈希槽机制把数据分散到多个实例上并通过重定向机制应对节点和槽位关系变化。本文主要梳理这两种方案的核心设计以及它们在定位目标上的差异。Redis 两种集群方式Redis 常见的集群方式主要有两种主从集群切片集群主从集群主从集群中主节点负责读写操作从节点主要负责复制主节点数据提供数据备份能力在常规主从模式下从节点通常不负责自动选主因此如果主节点宕机集群本身并不能自动完成故障恢复。哨兵模式哨兵模式可以理解为主从集群的一种升级。它的作用主要是监控主节点和从节点状态在主节点宕机后帮助完成自动选主因此哨兵模式重点解决的是高可用问题但哨兵模式本身并不解决数据水平扩展问题也不支持像切片集群那样基于数据分布做在线扩容。切片集群切片集群采用哈希槽来处理数据和实例之间的映射关系。一个 Redis Cluster 集群一共有16384 个哈希槽这些哈希槽可以理解成一组固定编号的数据分区。每个键值对都会根据它的 key被映射到其中一个哈希槽上然后由负责该槽的实例来存储和处理这个 key。所以切片集群重点解决的是数据水平扩展问题切片集群中的槽分布在部署 Redis Cluster 方案时可以使用cluster create命令创建集群。创建完成后Redis 会把 16384 个槽尽量平均分布在多个主节点实例上。例如如果集群中有N个主节点实例那么每个实例大致会负责16384 / N个槽这样一来数据就被切分到了不同实例中实现了横向扩展。切片集群中的从节点作用在 Redis Cluster 模式下从节点依然存在但它的角色是作为主节点的备用副本复制主节点数据在主节点故障时参与故障转移默认情况下从节点本身不承担正常读写服务也不是主要的数据访问入口。所以切片集群里主节点负责槽和数据从节点负责冗余和故障恢复哈希槽映射过程是怎样的Redis Cluster 中一个 key 会映射到哪个实例本质上要先经过“key - 槽”的映射。这个过程通常分为两步先根据 key 计算哈希值再根据哈希值映射到某个槽更具体地说Redis 会对 key 按照 CRC16 算法计算出一个 16 bit 的值再用这个值对 16384 取模最终得到一个0 ~ 16383范围内的数字这个数字就代表该 key 所属的哈希槽编号。所以key 先映射到槽槽再映射到实例这就是 Redis Cluster 的基本数据定位方式。切片集群客户端如何定位数据客户端和 Redis Cluster 实例建立连接后需要知道哪些哈希槽归哪个实例负责Redis Cluster 会在实例之间传播哈希槽分配信息因此各个实例都会保存整个集群的槽位映射关系。当客户端连接某个实例后这个实例也会把对应的槽分布信息返回给客户端。这样客户端本地就会缓存槽 - 实例的映射关系。之后客户端在发送请求时就可以根据 key 所属槽直接把命令发往正确实例。实例和哈希槽关系变更后客户端如何定位数据Redis Cluster 提供了重定向机制来解决这个问题。所谓“重定向”就是客户端把请求发给了一个实例但这个实例并不负责该 key 所在的槽于是实例告诉客户端应该去找哪一个新实例客户端再根据返回结果重新发起请求。这种机制主要出现在集群扩容集群缩容哈希槽迁移槽重新均衡等场景下。常见的变更原因实例与哈希槽对应关系发生变化通常有两个常见原因集群新增或删除实例需要重新分配槽为了做负载均衡需要在多个实例之间重新分布哈希槽也就是说只要槽位归属发生变化客户端原来缓存的槽位映射信息就可能失效。重定向机制Redis Cluster 常见的重定向主要有两种MOVEDASK它们虽然都表示“你要去别处访问”但语义并不一样。全部迁移MOVED如果某个槽的数据已经全部迁移完成那么客户端再访问旧实例时旧实例会返回MOVED例如GET hello:key (error) MOVED 13320 172.16.19.5:6379这表示槽13320已经正式迁移到172.16.19.5:6379客户端收到MOVED后需要做两件事把这次请求重新发到新的实例更新本地缓存中的“槽 - 实例”映射关系所以MOVED的含义是槽的归属已经正式改变了后续请求都应该发往新实例部分迁移ASK如果某个槽还处于迁移过程中那么客户端访问旧实例时可能会收到ASK例如GET hello:key (error) ASK 13320 172.16.19.5:6379这通常意味着这个槽并没有完全迁移完成只是当前访问的这个 key已经被迁移到了新实例此时客户端需要先向新实例发送ASKING再把真正的操作命令发给新实例但要注意客户端不会更新本地缓存中的槽位映射关系因为ASK只表示一次临时重定向而不是槽归属已经永久变化。ASK 和 MOVED 的区别这两个命令最核心的区别在于MOVED表示槽已经完成迁移客户端需要更新本地槽位缓存后续请求都要发往新实例ASK表示槽还在迁移过程中客户端只需要临时把这一次请求发往新实例不更新本地槽位缓存所以可以简单记忆为MOVED是永久重定向ASK是临时重定向主从哨兵和切片集群怎么选可以从目标上直接区分主从哨兵更适合解决单点故障自动故障切换高可用问题切片集群更适合解决单机内存不足单机吞吐不够需要水平扩展因此两者并不是简单替代关系而是哨兵偏高可用切片偏可扩展总结这篇文章可以压缩成几条核心结论Redis 集群常见有两种方式主从哨兵和切片集群主从哨兵主要解决高可用问题切片集群主要解决水平扩展问题Redis Cluster 使用 16384 个哈希槽实现 key 到实例的映射key 先通过 CRC16 计算再对 16384 取模确定所属槽客户端通过缓存槽位映射关系来定位数据MOVED表示槽已经正式迁移完成客户端需要更新缓存ASK表示槽仍在迁移中只做一次临时重定向理解 Redis 集群关键不是只记住“有哨兵”和“有哈希槽”而是要看清它们分别解决的是哪一类问题。如果这篇文章对你有帮助欢迎继续阅读本系列后续内容。若文中有不准确或需要补充的地方也欢迎指出。